====== Visitor Data Collection ======
A Visitor Spot in Blocks can be used as a basic entry point for visitors to connect to the system. This is essentially a URL allowing a number of visitors to all connect to the same Visitor Spot. The URL looks something like this:
https://pixi.guide/spot?mobile=Mob1
Where the protocol (http or https) and domain name (or IP address) parts vary depending on how your Blocks server is configured. The name following //?mobile=// is the name of the Visitor Spot. Often, such a URL is specified as the default URL for your Blocks server in the server configuration file, like this:
# Where to go if addressing root of server (i.e., just http://pixi.guide/)
serverRootRedirect: /spot/?mobile=Mob1
If you have something like this in your configuration file, it's sufficient for visitors to enter just the domain name in their browser, i.e.
pixi.guide
===== Anonymous versus Identified Visitors =====
Such a Visitor Spot can be configured either in an anonymous or visitor-specific manner. This is controlled by the "Identify Individual Visitors" setting under the Identity tab of the Visitor Spot's settings.
{{ :blocks:advanced_scripting:visitor_identity:visitor_identity_tab.png |}}
If "Identify Individual Visitors" is not selected, Blocks does not provide the ability to control or track individual visitors. You still have limited, collective control over some basic aspects, such as which root block being presented.
By selecting "Identify Individual Visitors" you gain two important abilities:
- For //each individual visitor// you gain independent control over child blocks, parameters, tags, custom CSS classes, etc, similar to what you have for Display Spots.
- The ability to collect data on each individual visitor, such as any data being entered by that visitor, spots being visited, game scores, etc.
By combining these two abilities, you can do things such as presenting different content to a visitor depending on where she's been during her visit, points earned along the way, or any other data provided or collected during the visit. Such data can also be archived after the visit for subsequent analyzation, e.g. to understand the route taken by visitors, which exhibits they visited, how long they stayed there, etc.
==== Means of Identification ====
When using individual visitor identification, Blocks supports a number of ways by which visitors can be identified:
- Using their mobile phone as the only identification.
- Using an identification tag, such as an RFID bracelet handed out at the reception.
- A combination of the above, where the RFID bracelet acts as the primary identification, and an optional mobile device can be associated with the same visitor for interaction and feedback purposes.
- Pre-qualified visitors, where each visitor is given a unique log-in code ahead of time (e.g., in an email). Additional data on visitor (such as name, company, etc) is pre-loaded into Blocks, and only pre-qualified visitors can gain access.
=== Using Mobile Phone as Only Identification ===
This is the easiest and most straightforward method of identifying and interacting with the visitor. It typically uses a Locator block to determine the whereabouts of the visitor, presenting relevant content and interaction at each spot along the way.
To use this method, do as follows:
- Create a Blocks user script (see below) defining the type of data to record.
- Select "Identify Individual Visitors" as discussed above.
- Enter the type-name of the data record in the "Record type" field.
=== Using an Identification Token ===
Here, a unique identification token is handed out to each visitor at the entrance. This token could for example be an RFID bracelet och tag, carried by the visitor. By scanning this token at stations, Blocks will learn who's where, and can collect and act on this information. The token may be returned when leaving, to be recycled for new visitors.
This arrangement removes the need for visitors to have and use a personal mobile phone. Depending on the information collected during the visit, this may also remove any privacy issues (assuming no personally identifiable information is collected). More on this below.
The visitor can still have a personalized experience, based on where she's been or interactions performed or information entered at visited spots. Personalized information can be shown on Display Spots as the visitor presents the identification token (e.g., scans the RFID bracelet).
To use this method, do as follows:
- Create a Blocks user script (see below) defining the type of data to record.
- Connect the desired number of token scanners (RFID, or similar) to Blocks.
- Scan the token when handing it out to the visitor.
- In the user script, create the visitor's data record, associating it with the token's ID.
Now, as the visitor scans the token at desired spots, Blocks will learn about this and can take actions as desired (including presenting personalized interactions at display spots).
To scan the tokens, you can use stand-alone scanners connected to the network, managed by a Device Driver in Blocks. Or use a Display Spot with a keyboard-emulating scanner connected to its USB port. Set the Display Spot to use "RFID/QR Keyboard Input" in its Advanced tab.
IMPORTANT: If tokens are reused, make sure tokens are disassociated from their visitors. This can be done by one or more of the following means:
* When returned at the exit, by canning it in a specific "exit scanner".
* When handed out to another visitor.
* En masse, for all tokens, once every night. This method assumes tokens aren't re-used during the day.
=== Using a Combination of Token and Mobile Phone ===
This method is essentially the same as the previous one, but with the additional ability of also associating a mobile phone with the same visitor. This provides basic tracking and personalized experience using the token, while an optional mobile phone enhances the experience, acting as a personal point of interaction and feedback.
To use this method, do as follows:
- Create a Blocks user script (see below) defining the type of data to record.
- Select "Identify Individual Visitors" as discussed above.
- Enter the name of the data record in the "Record type" field.
- Select "Mobile is Secondary ID", and enter the name of a record field marked with @id().
- Connect the desired number of token scanners to Blocks.
- Scan the token when handing it out to the visitor.
- In the user script, create the visitor's data record, associating it with the token's ID.
- Optionally, also scan the visitors mobile phone. This can be done using a QR code shown on the phone. This links the mobile phone to the same data record as identified by the token.
You'll then use the token at each station to identify yourself. The optional mobile phone can be used as feedback (e.g., scores at game stations) or to enter additional information that goes into the visitor's data record and/or affects functions in the user script.
==== Privacy Issues ====
Depending on what information you collect and the laws governing your area, you may need to pay attention to privacy regulations, such as europe's GDPR law. This can be done by presenting a notice on visitors' mobile devices that must be agreed to before registering, or similar.
===== User Script =====
To create a //personalized visitor journey//, as outlined above, you need a Blocks [[blocks:advanced_scripting|user script]]. This script defines the data that can be collected. It also decides what will happen as visitors arrive at interaction spots.
==== Data Record Declaration ====
A key feature of this user script is to declare what data to be collected from visitors. This is done using a //record declaration//, typically located at the beginning of the script. Here's an example of such a record declaration:
@record("Data we track for each visitor")
class ExampleRecord extends RecordBase {
@field() whenJoined: number; // Time when first connected
@field() station: string; // Currently (or last) visited station
@field() heardSound: boolean; // The visitor has been at the sound station
@field() @spotParameter() visitorName: string; // Visit's desired nickname
@field() @spotParameter() quizScore: number; // Score in our Quiz game
}
The name of this record type is "ExampleRecord". That's the name to enter in the "Record type" field of the Identity tab in the Visitor Spot's settings. The declaration shown above has five data fields. What data fields to use, their names and data types, depend entirely on what information you want to collect. Each data field must be marked with either a @field() or an @id() decorator, where @id() indicates a unique ID field (a "secondary key" in database-speak).
In addition to the fields mentioned above, each record also has a built-in, unique identity field named //$puid//, declared in RecordBase. This is the base class of ExampleRecord, as you can see above. All custom record declarations must extend RecordBase. This $puid uniquely identifies each data record. When using a visitor's mobile phone as the only identification, this matches the identity ID assigned to the phone when it first connects to Blocks through this mobile spot's URL.
=== Record Field Decorators ===
As mentioned above, each field in the record must be marked with either a @field() an @id() decorator. In addition to these mandatory decorators, you may optionally add a @spotParameter() decorator.
The **@id()** decorator indicates that the field is to be used as a secondary key (an "index" in database terms), allowing the record to be looked up also by this value. This index is in addition to the standard $puid field mentioned above. This is useful in cases where you have other means of identifying the visitor than just a mobile phone. For instance, when using an RFID tag, you'll want the ID number of the visitor's tag stored in a field marked with an @id() decorator. Note that the value in a @id() field //must// be unique for any given record type. Thus, if you re-cycle RFID tags, you //must// delete (or archive) any previous record using that same RFID code before assigning it to an @id() field of a new visitor. More on how to delete/archive records below. Use the @id() decorator on its own – do not combine it with @field().
A **@spotParameter()** decorator causes data stored in the record to be linked to a Visitor Spot parameter with the same name and data type. This must be used together with the @field() decorator, as shown above. You must establish this parameter separately, making sure it has the same name and data type as the record field. This must be done in the Block assigned to the Visitor Spot (here accessible as a //Local// parameter). Once you've linked the record field to a spot parameter in this way, any changes made to the parameter will be reflected in the data record field and vice versa. This could, for example, be used to allow a visitor to enter a nickname on her phone, or other relevant information, then automatically stored and persisted in the visitor's data record.
==== Creating and Finding Records ====
When using a mobile phone (i.e., a Visitor Spot) as the only identifier of a visitor, the associated data record is implicitly created when the visitor connects to Blocks. A 'visitor' event is fired on the MobileSpot to indicate this. This event provides a Visitor object, which represents the individual visitor's mobile phone. The Visitor object provides access to the data record, allowing you to use this right away.
When using other means of visitor identification, such as RFID, NFC or other such tokens, there's no automatic record creation. You must then listen for the ID to arrive from the token scanner. You can use a Display Spot to connect many types of such scanners, often exposing the scanned code as its //scannerInput// property- Alternatively, a Device Driver can be used to accept a code from a scanner connected to your network. Once the scanned code appears, you can either use it to look up the corresponding record (assuming it exists), using the //getRecordSec// script function. Alternatively, you can create the record using the //newRecord// Script function. Then remember to immediately store the scanner ID in an @id() field in the record, allowing you to find it later using that ID.
When using a combination of identity token and mobile devices, you need to find a way by which both these methods of identification can be bound to the same record. In this case, you must use two separate @id() fields, one holding the token's ID and the other the Visitor's phone identity (available in the //identity// field of the associated Visitor object). It's often non-trivial to determine which phone belongs to which token. This can be handled, for example, using dynamically generated QR code shown on the check-in station as the ID token is handed out, where the visitor needs to scan this QR code with her phone to establish the connection. The QR code will then provide both the URL used to access Blocks and the associated token ID (as a query parameter), allowing these two to be set on the same record.
=== Visitor Lifecycle ===
Note that the Visitor object, provided when a visitor's phone connects to Blocks, is available //only as long as the visitor's phone remains connected//. If the phone disconnects (e.g., the visitor turns it off or closes the web page), the Visitor object is discarded. This is indicated by the 'finish' event being emitted by the Visitor object. You must not hold on to or use a Visitor object beyond this point. Thus, it is important that you listen for this event and release all references to the Visitor object when this event fires. Furthermore, if you open any other event or property subscriptions associated with the Visitor object, you must close those when the Visitor goes away. Doing so avoids memory leaks and the possibility "ghost calls" into code that's no longer supposed to be alive.
While the Visitor object will come and go as the visitor's phone connects and disconnects, any associated data record remains intact. hence this data record should hold all "important" data associated with a visitor. The Visitor object can be used to communicate with the individual visitor's phone, either by making explicit calls to functions available on the Visitor object or by changing the value of a record field marked with //@spotParameter()//, which will then be reflected by any Spot Parameter with the same name and type. The value of such a Spot Parameter can be used in a variety of ways on the Spot; e.g. to show information to the visitor, acting as data input, affecting property bindings, etc.
==== Data Record Storage ====
In addition to being available inside your user script, data records are also stored on disk inside the //record// directory located in your Blocks root directory. Inside this record directory, Blocks creates one sub-directory per record type, named by the record type ("ExampleRecord" in the example shown above). Inside such a type-specific subdirectory, you'll find one directory per record instance. This is named with //Pn// where //n// is the $puid identifier mentioned above.
Inside that record instance directory, you'll find at least a file named "log.tsv". This is a log file of all data stored or updated in the data record, along with a time stamp for each action. This file uses the popular TSV format (a variant of the more well-known CSV format, but using a tabs as separators rather than commas). For each change applied to the data record, a new line is appended to this file, describing what was changed, when the change was made and what the new value is. This data is used by Blocks to resurrect the data in case you must restart the Blocks server while there are active visitors. It can also be used to analyze visitor behavior, by deriving data such as:
* Time of arrival.
* Spots visited, and in what order.
* How long did the visitor stay at each spot?
* Name, game scores, or any other data you chose to collect along the way.
You must not remove or change this file while the visitor is still active.
==== Data Record Clean-up ====
Once a visitor leaves, this data must be discarded to not consume excessive amounts of server memory as new visitors join in. Your user script must manage this clean-up of visitor data records, either one by one (as each visitor leaves or any identifying token is recycled) or en masse once closed for the day (e.g., at 3:00am every day).
This data record clean-up can either simply delete the record(s) or archive them. Use the archive method if you intend to use the data for further analysis of visitor's behaviors. If this is not desired, the data can be completely erased. This latter method also helps alleviate any privacy-related concerns, since no data is being retained past the visit. Archived data sets are moved into a subdirectory named "archive" found inside the data set's directory.
To remove individual records, call the **deleteRecord** function on the user script, passing it the record to discard. Pass //true// as the second parameter to archive the record rather than deleting it. To remove //all// records of a particular type, call the **deleteRecords** function on the user script, passing it the record type and a second optional boolean parameter set to true to archive the records (if desired). See the declarations of these functions in the Script.ts file in system_lib for details.
==== Other User Script Functions ====
In addition to declaring the record type, as described above, your user script must contain at least the following functionality:
* An exported class with the same name as the user script file, extending the //Script// base class, along with a constructor that calls //super// with the ScriptEnv passed to the constructor. This is a requirement of all user scripts.
* A mechanism to clean up data records, as described above. This can be done one by one as visitors leave or tokens are recycled, or en masse once every night. Such a nightly clean-up can be exposed as a public function marked with @callable(), which is then called by a task scheduled to run once every night.
In addition to these mandatory functions, you most likely will have additional functions performing your desired logic as visitors arrive at spots, interact with objects, enter data on their mobile phones, etc. The details here depend entirely on your own requirements.
===== Where to go from Here =====
Here are a few [[blocks:app-note#personal_visitor_experience|application notes]] you can download and run to see how this works in practice. You can learn from these application notes, and perhaps use one of them as a starting point for building your own Personal Visitor Experience.