Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stroop improvements #1417

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions ResearchKit/ActiveTasks/ORKStroopContentView.h
Expand Up @@ -39,6 +39,7 @@ NS_ASSUME_NONNULL_BEGIN

@interface ORKStroopContentView : ORKActiveStepCustomView

@property (nonatomic) NSString * timeoutText;
@property (nonatomic) NSString * colorLabelText;
@property (nonatomic) UIColor * colorLabelColor;
@property (nonatomic) ORKBorderedButton * RButton;
Expand Down
25 changes: 23 additions & 2 deletions ResearchKit/ActiveTasks/ORKStroopContentView.m
Expand Up @@ -40,6 +40,7 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
static const CGFloat buttonStackViewSpacing = 20.0;

@implementation ORKStroopContentView {
UILabel *_timeoutView;
UILabel *_colorLabel;
UIStackView *_buttonStackView;
}
Expand All @@ -55,6 +56,16 @@ - (instancetype)initWithFrame:(CGRect)frame {
[_colorLabel setFont:[UIFont systemFontOfSize:60]];
[_colorLabel setAdjustsFontSizeToFitWidth:YES];

if (!_timeoutView) {
_timeoutView = [UILabel new];
_timeoutView.numberOfLines = 1;
_timeoutView.textAlignment = NSTextAlignmentCenter;
[_timeoutView setTextColor:[UIColor blueColor]];
[_timeoutView setFont:[UIFont systemFontOfSize:20]];
[_timeoutView setAdjustsFontSizeToFitWidth:YES];
_timeoutView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:_timeoutView];
}

_RButton = [[ORKBorderedButton alloc] init];
_RButton.translatesAutoresizingMaskIntoConstraints = NO;
Expand All @@ -80,13 +91,19 @@ - (instancetype)initWithFrame:(CGRect)frame {
_buttonStackView.axis = UILayoutConstraintAxisHorizontal;

[self addSubview:_colorLabel];
[self addSubview:_timeoutView];
[self addSubview:_buttonStackView];

[self setUpConstraints];
}
return self;
}

- (void)setTimeoutText:(NSString *)timeoutText {
[_timeoutView setText:timeoutText];
[self setNeedsDisplay];
}

- (void)setColorLabelText:(NSString *)colorLabelText {
[_colorLabel setText:colorLabelText];
[self setNeedsDisplay];
Expand All @@ -97,6 +114,10 @@ - (void)setColorLabelColor:(UIColor *)colorLabelColor {
[self setNeedsDisplay];
}

- (NSString *)timeoutText {
return _timeoutView.text;
}

- (NSString *)colorLabelText {
return _colorLabel.text;
}
Expand All @@ -108,9 +129,9 @@ - (UIColor *)colorLabelColor {
- (void)setUpConstraints {

NSMutableArray *constraints = [[NSMutableArray alloc] init];
NSDictionary *views = NSDictionaryOfVariableBindings(_colorLabel, _buttonStackView);
NSDictionary *views = NSDictionaryOfVariableBindings(_colorLabel, _timeoutView, _buttonStackView);

[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(==30)-[_colorLabel]-(>=10)-[_buttonStackView]-(==30)-|"
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(==30)-[_colorLabel]-[_timeoutView]-(>=10)-[_buttonStackView]-(==30)-|"
options:NSLayoutFormatAlignAllCenterX
metrics:nil
views:views]];
Expand Down
34 changes: 33 additions & 1 deletion ResearchKit/ActiveTasks/ORKStroopResult.h
Expand Up @@ -34,7 +34,7 @@
NS_ASSUME_NONNULL_BEGIN

/**
The `ORKStroopResult` class represents the result of a single successful attempt within an ORKStroopStep.
The `ORKStroopResult` class represents the result of each stimulus within an ORKStroopStep as well as results that change over the entire task.

A stroop result is typically generated by the framework as the task proceeds. When the task completes, it may be appropriate to serialize the sample for transmission to a server or to immediately perform analysis on it.
*/
Expand All @@ -51,6 +51,11 @@ ORK_CLASS_AVAILABLE
*/
@property (nonatomic, assign) NSTimeInterval endTime;

/**
The `reactionTime` property is the time taken (in seconds) for a response to a stimulus, equal to the difference between startTime and endTime.
*/
@property (nonatomic, assign) double reactionTime;

/**
The `color` property is the color of the question string.
*/
Expand All @@ -66,6 +71,33 @@ ORK_CLASS_AVAILABLE
*/
@property (nonatomic, copy, nullable) NSString *colorSelected;

/**
The 'match' property is a Boolean value indicating whether the value of colorSelected matches that of color.
The value of this property is `YES` when there is a match, and `NO` otherwise.
*/
@property (nonatomic, assign) BOOL match;

/**
The 'timedOut' property is a Boolean value indicating whether the the attempt timed out, based on the value set in the non-zero 'timeout' parameter (in seconds), before a selection was made. The value of this property is `YES` when the timeout value was reached, and `NO` otherwise. When YES, reactionTime values will not contribute to task summaries (means and standard deviations).
*/
@property (nonatomic, assign) BOOL timedOut;

/**
The `percentCorrect` property is the percentage of correct 'match' answers for all images in the task so far . This updates with every image presented in the set.
*/
@property (nonatomic, assign) double percentCorrect;

/**
The `meanReactionTime` property is the mean (average) of reactionTime (in seconds). This updates with every word presented in the task.
*/
@property (nonatomic, assign) double meanReactionTime;

/**
The `stdReactionTime` property is the standard deviation (a measure of distribution) of reactionTime (in seconds). This updates with every word presented in the set.
*/
@property (nonatomic, assign) double stdReactionTime;


@end

NS_ASSUME_NONNULL_END
Expand Down
44 changes: 34 additions & 10 deletions ResearchKit/ActiveTasks/ORKStroopResult.m
Expand Up @@ -36,21 +36,33 @@ @implementation ORKStroopResult

- (void)encodeWithCoder:(NSCoder *)aCoder {
[super encodeWithCoder:aCoder];
ORK_ENCODE_DOUBLE(aCoder, startTime);
ORK_ENCODE_DOUBLE(aCoder, endTime);
ORK_ENCODE_OBJ(aCoder, color);
ORK_ENCODE_OBJ(aCoder, text);
ORK_ENCODE_OBJ(aCoder, colorSelected);
ORK_ENCODE_BOOL(aCoder, match);
ORK_ENCODE_BOOL(aCoder, timedOut);
ORK_ENCODE_DOUBLE(aCoder, percentCorrect);
ORK_ENCODE_DOUBLE(aCoder, startTime);
ORK_ENCODE_DOUBLE(aCoder, endTime);
ORK_ENCODE_DOUBLE(aCoder, reactionTime);
ORK_ENCODE_DOUBLE(aCoder, meanReactionTime);
ORK_ENCODE_DOUBLE(aCoder, stdReactionTime);
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
ORK_DECODE_DOUBLE(aDecoder, startTime);
ORK_DECODE_DOUBLE(aDecoder, endTime);
ORK_DECODE_OBJ_CLASS(aDecoder, color, NSString);
ORK_DECODE_OBJ_CLASS(aDecoder, text, NSString);
ORK_DECODE_OBJ_CLASS(aDecoder, colorSelected, NSString);
ORK_DECODE_BOOL(aDecoder, match);
ORK_DECODE_BOOL(aDecoder, timedOut);
ORK_DECODE_DOUBLE(aDecoder, percentCorrect);
ORK_DECODE_DOUBLE(aDecoder, startTime);
ORK_DECODE_DOUBLE(aDecoder, endTime);
ORK_DECODE_DOUBLE(aDecoder, reactionTime);
ORK_DECODE_DOUBLE(aDecoder, meanReactionTime);
ORK_DECODE_DOUBLE(aDecoder, stdReactionTime);
}
return self;
}
Expand All @@ -64,25 +76,37 @@ - (BOOL)isEqual:(id)object {

__typeof(self) castObject = object;
return (isParentSame &&
(self.startTime == castObject.startTime) &&
(self.endTime == castObject.endTime) &&
ORKEqualObjects(self.color, castObject.color) &&
ORKEqualObjects(self.text, castObject.text) &&
ORKEqualObjects(self.colorSelected, castObject.colorSelected));
ORKEqualObjects(self.colorSelected, castObject.colorSelected) &&
(self.match == castObject.match) &&
(self.timedOut == castObject.timedOut) &&
(self.percentCorrect == castObject.percentCorrect) &&
(self.startTime == castObject.startTime) &&
(self.endTime == castObject.endTime) &&
(self.reactionTime == castObject.reactionTime) &&
(self.meanReactionTime == castObject.meanReactionTime) &&
(self.stdReactionTime == castObject.stdReactionTime));
}

- (instancetype)copyWithZone:(NSZone *)zone {
ORKStroopResult *result = [super copyWithZone:zone];
result.startTime = self.startTime;
result.endTime = self.endTime;
result -> _color = [self.color copy];
result -> _text = [self.text copy];
result -> _colorSelected = [self.colorSelected copy];
result.match = self.match;
result.timedOut = self.timedOut;
result.percentCorrect = self.percentCorrect;
result.startTime = self.startTime;
result.endTime = self.endTime;
result.reactionTime = self.reactionTime;
result.meanReactionTime = self.meanReactionTime;
result.stdReactionTime = self.stdReactionTime;
return result;
}

- (NSString *)descriptionWithNumberOfPaddingSpaces:(NSUInteger)numberOfPaddingSpaces {
return [NSString stringWithFormat:@"%@; color: %@; text: %@; colorselected: %@ %@", [self descriptionPrefixWithNumberOfPaddingSpaces:numberOfPaddingSpaces], self.color, self.text, self.colorSelected, self.descriptionSuffix];
return [NSString stringWithFormat:@"%@; color: %@; text: %@; colorselected: %@; match: %d; timedOut: %d; percentCorrect: %f; reactionTime: %f; meanReactionTime: %f; stdReactionTime: %f %@", [self descriptionPrefixWithNumberOfPaddingSpaces:numberOfPaddingSpaces], self.color, self.text, self.colorSelected, self.match, self.timedOut, self.percentCorrect, self.reactionTime, self.meanReactionTime, self.stdReactionTime, self.descriptionSuffix];
}

@end
6 changes: 6 additions & 0 deletions ResearchKit/ActiveTasks/ORKStroopStep.h
Expand Up @@ -39,6 +39,12 @@ ORK_CLASS_AVAILABLE

@property (nonatomic, assign) NSInteger numberOfAttempts;

@property (nonatomic, assign) NSTimeInterval minimumInterStimulusInterval;

@property (nonatomic, assign) NSTimeInterval maximumInterStimulusInterval;

@property (nonatomic, assign) NSTimeInterval timeout;

@end

NS_ASSUME_NONNULL_END
30 changes: 29 additions & 1 deletion ResearchKit/ActiveTasks/ORKStroopStep.m
Expand Up @@ -61,6 +61,21 @@ - (void)validateParameters {
if (self.numberOfAttempts < minimumAttempts) {
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:[NSString stringWithFormat:@"number of attempts should be greater or equal to %ld.", (long)minimumAttempts] userInfo:nil];
}
if (self.minimumInterStimulusInterval <= 0) {
@throw [NSException exceptionWithName:NSInvalidArgumentException
reason:@"minimumInterStimulusInterval must be greater than zero"
userInfo:nil];
}
if (self.maximumInterStimulusInterval < self.minimumInterStimulusInterval) {
@throw [NSException exceptionWithName:NSInvalidArgumentException
reason:@"maximumInterStimulusInterval cannot be less than minimumInterStimulusInterval"
userInfo:nil];
}
if (self.timeout <= 0) {
@throw [NSException exceptionWithName:NSInvalidArgumentException
reason:@"timeout must be greater than zero"
userInfo:nil];
}
}

- (BOOL)startsFinished {
Expand All @@ -74,27 +89,40 @@ - (BOOL)allowsBackNavigation {
- (instancetype)copyWithZone:(NSZone *)zone {
ORKStroopStep *step = [super copyWithZone:zone];
step.numberOfAttempts = self.numberOfAttempts;
step.minimumInterStimulusInterval = self.minimumInterStimulusInterval;
step.maximumInterStimulusInterval = self.maximumInterStimulusInterval;
step.timeout = self.timeout;
return step;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self ) {
ORK_DECODE_INTEGER(aDecoder, numberOfAttempts);
ORK_DECODE_DOUBLE(aDecoder, minimumInterStimulusInterval);
ORK_DECODE_DOUBLE(aDecoder, maximumInterStimulusInterval);
ORK_DECODE_DOUBLE(aDecoder, timeout);
}
return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
[super encodeWithCoder:aCoder];
ORK_ENCODE_INTEGER(aCoder, numberOfAttempts);
ORK_ENCODE_DOUBLE(aCoder, minimumInterStimulusInterval);
ORK_ENCODE_DOUBLE(aCoder, maximumInterStimulusInterval);
ORK_ENCODE_DOUBLE(aCoder, timeout);
}

- (BOOL)isEqual:(id)object {
BOOL isParentSame = [super isEqual:object];

__typeof(self) castObject = object;
return (isParentSame && (self.numberOfAttempts == castObject.numberOfAttempts));
return (isParentSame &&
(self.numberOfAttempts == castObject.numberOfAttempts) &&
(self.minimumInterStimulusInterval == castObject.minimumInterStimulusInterval) &&
(self.maximumInterStimulusInterval == castObject.maximumInterStimulusInterval) &&
(self.timeout == castObject.timeout));
}

@end