Skip to content

Commit

Permalink
Add Scope, Cardinality, Type, and DictionaryItem classes (#6938)
Browse files Browse the repository at this point in the history
This adds the classes representing the data scope, data type,
data cardinality, DictionaryItem (and dictionary Category) from
PR #6936 but does not use yet use them.

It gives us the ability to describe data dictionaries in LORIS.
  • Loading branch information
driusan authored Dec 9, 2020
1 parent f357300 commit 5fa9a04
Show file tree
Hide file tree
Showing 5 changed files with 363 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ changes in the following format: PR #1234***

##LORIS 24.0 (Release Date: ??)
### Core
- New classes to describe a data dictionary (PR #6938)
#### Features
- Data tables may now stream data as they're loading rather than waiting
until all data has loaded. (PR #6853)
Expand Down
100 changes: 100 additions & 0 deletions src/Data/Cardinality.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php
namespace LORIS\Data;

/**
* Cardinality represents the number of data points which
* apply to the scope of a data type.
*
* Since the Cardinality class represents an enumeration, the
* class is final.
*
* @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
*/
final class Cardinality implements \JsonSerializable
{
// Valid cardinality types for data to apply to.

/**
* A Unique Cardinality signifies that the data is unique
* across the scope. Examples of unique data are CandID
* for the candidate scope or VisitLabel for the Session
* scope.
*/
const UNIQUE = 1;

/**
* A Single Cardinality signifies that each data point in
* the scope should have exactly one value. For instance,
* date of birth for a candidate in the candidate scope.
*/
const SINGLE = 2;

/**
* An Optional Cardinality signifies that each data point
* in the scope may have zero or one value. For instance,
* the date of death for a candidate in the candidate scope.
*/
const OPTIONAL = 3;

/**
* A Many Cardinality signifies that each data point will
* have zero or more values associated. For instance,
* the T1 scans acquired at a session.
*/
const MANY = 4;

protected $cardinality;

/**
* Constructs a Scope object. $scope should be a class constant
* to construct the scope for, not an int literal.
*
* @param int $scope The scope
*/
public function __construct(int $card)
{
switch ($card) {
case self::UNIQUE: // fallthrough
case self::SINGLE: // fallthrough
case self::OPTIONAL: // fallthrough
case self::MANY: // fallthrough
$this->cardinality = $card;
break;
default:
throw new \DomainException("Invalid cardinality");
}
}

/**
* Convert the enumeration from a memory-friendly integer to a
* human-readable string when used in a string context.
*
* @return string
*/
public function __toString() : string
{
switch ($this->cardinality) {
case self::UNIQUE: // fallthrough
return "unique";
case self::SINGLE: // fallthrough
return "single";
case self::OPTIONAL: // fallthrough
return "optional";
case self::MANY: // fallthrough
return "many";
default:
return "invalid cardinality";
}
}

/**
* Implement the JsonSerializable interface by
* converting to a string
*
* @return string
*/
public function jsonSerialize() : string
{
return $this->__toString();
}
}
78 changes: 78 additions & 0 deletions src/Data/Dictionary/Category.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace LORIS\Data\Dictionary;

/**
* A \LORIS\Data\Dictionary\Category represents a grouping of
* DictionaryItems.
*
* @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
*/
class Category
{
protected $name;
protected $description;
protected $items = null;

/**
* Construct a dictionary Category
*
* @param string $name The machine name of the category
* @param string $desc The human readable description of
* the category
* @param ?DictionaryItem[] $items An optional iterable of items which
* the category contains.
*/
public function __construct(string $name, string $desc, ?iterable $items = null)
{
$this->name = $name;
$this->description = $desc;
$this->items = $items;
}

/**
* Return the name of the Category
*
* @return string
*/
public function getName() : string
{
return $this->name;
}

/**
* Return the human readable description of the Category
*
* @return string
*/
public function getDescription() : string
{
return $this->description;
}

/**
* Return the items which belong to the Category
*
* @return ?DictionaryItem[]
*/
public function getItems() : ?iterable
{
return $this->items;
}

/**
* Returns a new Category identical to this category, but with
* the items populated with $items. This can be used when the items
* were not yet known at the time the constructor was called.
*
* @param DictionaryItem[] $items The items to add to the new Category
*
* @return Category
*/
public function withItems(iterable $items) : Category
{
$c = clone($this);
$c->items = $items;
return $c;
}
}
117 changes: 117 additions & 0 deletions src/Data/Dictionary/DictionaryItem.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php
declare(strict_types=1);
namespace LORIS\Data\Dictionary;

use \LORIS\Data\Scope;
use \LORIS\Data\Type;
use \LORIS\Data\Cardinality;

/**
* A DictionaryItem represents a description of a type of data
* managed by LORIS.
*
* @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
*/
class DictionaryItem implements \LORIS\StudyEntities\AccessibleResource
{
protected $name;
protected $description;
protected $scope;
protected $type;

/**
* Construct a DictionaryItem with the given parameters
*
* @param string $name The field name of the dictionary item
* @param string $desc The dictionary item's description
* @param Scope $scope The scope to which this DictionaryItem
* applies
* @param Type $t The data type of this dictionary item
* @param Cardinality $c The data cardinality
*/
public function __construct(
string $name,
string $desc,
Scope $scope,
Type $t,
Cardinality $c
) {
$this->name = $name;
$this->description = $desc;
$this->scope = $scope;
$this->type = $t;
$this->cardinality = $c;
}

/**
* Return the field name of this DictionaryItem
*
* @return string
*/
public function getName() : string
{
return $this->name;
}

/**
* Return a human readable description of this DictionaryItem.
*
* @return string
*/
public function getDescription() : string
{
return $this->description;
}

/**
* Return the data scope at which the data for this DictionaryItem
* applies.
*
* @return Scope
*/
public function getScope() : Scope
{
return $this->scope;
}

/**
* Return the data type for the data which this DictionaryItem
* describes.
*
* @return \LORIS\Data\Type
*/
public function getDataType() : \LORIS\Data\Type
{
return $this->type;
}

/**
* Return the data cardinality of this DictionaryItem. ie. for
* each entity of type Scope how many pieces of data should
* exist for this DictionaryItem.
*
* @return \LORIS\Data\Cardinality
*/
public function getCardinality() : \LORIS\Data\Cardinality
{
return $this->cardinality;
}

/**
* The DictionaryItem instance implements the AccessibleResource
* interface in order to make it possible to restrict items per
* user. However, by default DictionaryItems are accessible by
* all users. In order to restrict access to certain items, a
* module would need to extend this class and override the
* isAccessibleBy method with its prefered business logic.
*
* @param \User $user The user whose access should be
* validated
*
* @return bool
*/
public function isAccessibleBy(\User $user): bool
{
return true;
}
}
67 changes: 67 additions & 0 deletions src/Data/Scope.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php
namespace LORIS\Data;

/**
* A Scope is an enumeration class which represents the scope
* that a piece of data may apply to in LORIS.
*
* The Scope class is final because the list of enumeration types
* can not be dynamically extended without modifying all places
* that must deal with the enumeration options.
*
* @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
*/
final class Scope implements \JsonSerializable
{
// Valid scopes for data to apply to.
const CANDIDATE = 1;
const SESSION = 2;

/**
* The value of the current scope instance
*/
protected $scope;

/**
* Constructs a Scope object. $scope should be a class constant
* to construct the scope for, not an int literal.
*
* @param int $scope The scope
*/
public function __construct(int $scope)
{
switch ($scope) {
case self::CANDIDATE: // fallthrough
case self::SESSION:
$this->scope = $scope;
break;
default:
throw new \DomainException("Invalid scope");
}
}

/**
* Convert the enumeration from a memory-friendly integer to a
* human-readable string when used in a string context.
*
* @return string
*/
public function __toString() : string
{
switch ($this->scope) {
case self::CANDIDATE:
return "candidate";
case self::SESSION:
return "session";
default:
// This shouldn't happen since the constructor threw an
// exception for an invalid value.
return "invalid scope";
}
}

public function jsonSerialize()
{
return $this->__toString();
}
}

0 comments on commit 5fa9a04

Please sign in to comment.