Skip to content

Commit

Permalink
add method to check if profil ids in study with condition
Browse files Browse the repository at this point in the history
  • Loading branch information
phev8 committed May 28, 2020
1 parent 81c6c9c commit 651afb3
Show file tree
Hide file tree
Showing 2 changed files with 207 additions and 12 deletions.
59 changes: 55 additions & 4 deletions pkg/service/studyinfo_endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"log"

"github.com/influenzanet/study-service/pkg/api"
"github.com/influenzanet/study-service/pkg/studyengine"
"github.com/influenzanet/study-service/pkg/types"
"github.com/influenzanet/study-service/pkg/utils"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -82,8 +84,57 @@ func (s *studyServiceServer) GetActiveStudies(ctx context.Context, req *api.Toke
return resp, nil
}

func (s *studyServiceServer) HasParticipantStateWithCondition(ctx context.Context, req *api.ProfilesWithConditionReq) (*api.AssignedSurveys, error) {
// check if study has participant
// check if participant state satisifies conditions
return nil, status.Error(codes.Unimplemented, "unimplemented")
func (s *studyServiceServer) HasParticipantStateWithCondition(ctx context.Context, req *api.ProfilesWithConditionReq) (*api.ServiceStatus, error) {
if req == nil || req.StudyKey == "" || len(req.ProfileIds) < 1 {
return nil, status.Error(codes.InvalidArgument, "missing argument")
}

study, err := s.studyDBservice.GetStudyByStudyKey(req.InstanceId, req.StudyKey)
if err != nil {
return nil, status.Error(codes.Internal, "study not found")
}

for _, profileID := range req.ProfileIds {
// ParticipantID
participantID, err := utils.ProfileIDtoParticipantID(profileID, s.StudyGlobalSecret, study.SecretKey)
if err != nil {
continue
}

pState, err := s.studyDBservice.FindParticipantState(req.InstanceId, study.Key, participantID)
if err != nil {
// profile not in the study
continue
}

cond := types.ExpressionArgFromAPI(req.Condition)
if cond.IsExpression() {
evalCtx := studyengine.EvalContext{
ParticipantState: pState,
}
resp, err := studyengine.ExpressionEval(*cond.Exp, evalCtx)
if err != nil {
log.Printf("HasParticipantStateWithCondition.ExpressionEval: %v", err)
// profile not in the study
continue
}
bVal, ok := resp.(bool)
if ok && bVal {
return &api.ServiceStatus{
Version: apiVersion,
Status: api.ServiceStatus_NORMAL,
Msg: "participant found in study",
}, nil
}
//log.Println(pState)
} else if cond.Num > 0 {
// hardcoded true
return &api.ServiceStatus{
Version: apiVersion,
Status: api.ServiceStatus_NORMAL,
Msg: "participant found in study",
}, nil
}
}
return nil, status.Error(codes.NotFound, "no participant found")
}
160 changes: 152 additions & 8 deletions pkg/service/studyinfo_endpoints_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package service
import (
"context"
"testing"
"time"

"github.com/influenzanet/study-service/pkg/api"
"github.com/influenzanet/study-service/pkg/types"
Expand Down Expand Up @@ -214,17 +215,160 @@ func TestGetActiveStudiesEndpoint(t *testing.T) {
})
}
func TestHasParticipantStateWithConditionEndpoint(t *testing.T) {
/*s := studyServiceServer{
s := studyServiceServer{
globalDBService: testGlobalDBService,
studyDBservice: testStudyDBService,
StudyGlobalSecret: "globsecretfortest1234",
}*/
}

// create study for user in it
testStudies := []types.Study{
{
Status: "active",
Key: "studyfor_hasParticipantWithConditionStudies_1",
SecretKey: "testsecret",
},
}

for _, study := range testStudies {
_, err := testStudyDBService.CreateStudy(testInstanceID, study)
if err != nil {
t.Errorf("unexpected error: %s", err.Error())
return
}
}

testProfileID1 := "234234laaabbb3423_for_hasProf_1"
testProfileID2 := "234234laaabbb3423_for_hasProf_2"

pid1, err := s.profileIDToParticipantID(testInstanceID, testStudies[0].Key, testProfileID1)
if err != nil {
t.Errorf("unexpected error: %s", err.Error())
return
}
pid2, err := s.profileIDToParticipantID(testInstanceID, testStudies[0].Key, testProfileID2)
if err != nil {
t.Errorf("unexpected error: %s", err.Error())
return
}

pState1 := types.ParticipantState{
ParticipantID: pid1,
StudyStatus: "active",
LastSubmissions: map[string]int64{
"s3": time.Now().Unix() - 20,
},
AssignedSurveys: []types.AssignedSurvey{
{SurveyKey: "s1"},
},
}

pState2 := types.ParticipantState{
ParticipantID: pid2,
StudyStatus: "exited",
}

// test with nil
// test with empty
// test with user profiles not in the study
// test with profiles not fulfilling condition
// test with profiles mathing conditions
t.Error("test unimplemented")
_, err = s.studyDBservice.SaveParticipantState(testInstanceID, testStudies[0].Key, pState1)
if err != nil {
t.Errorf("unexpected error: %s", err.Error())
return
}
_, err = s.studyDBservice.SaveParticipantState(testInstanceID, testStudies[0].Key, pState2)
if err != nil {
t.Errorf("unexpected error: %s", err.Error())
return
}

t.Run("with missing request", func(t *testing.T) {
_, err := s.HasParticipantStateWithCondition(context.Background(), nil)
ok, msg := shouldHaveGrpcErrorStatus(err, "missing argument")
if !ok {
t.Error(msg)
}
})

t.Run("with empty request", func(t *testing.T) {
_, err := s.HasParticipantStateWithCondition(context.Background(), &api.ProfilesWithConditionReq{})
ok, msg := shouldHaveGrpcErrorStatus(err, "missing argument")
if !ok {
t.Error(msg)
}
})

t.Run("with user profiles not in the study", func(t *testing.T) {
_, err := s.HasParticipantStateWithCondition(context.Background(), &api.ProfilesWithConditionReq{
ProfileIds: []string{"notthere1", "notthere2"},
StudyKey: testStudies[0].Key,
InstanceId: testInstanceID,
Condition: &api.ExpressionArg{Dtype: "num", Data: &api.ExpressionArg_Num{Num: 1}},
})
ok, msg := shouldHaveGrpcErrorStatus(err, "no participant found")
if !ok {
t.Error(msg)
}
})

t.Run("with profiles not fulfilling condition", func(t *testing.T) {
_, err := s.HasParticipantStateWithCondition(context.Background(), &api.ProfilesWithConditionReq{
ProfileIds: []string{testProfileID2},
StudyKey: testStudies[0].Key,
InstanceId: testInstanceID,
Condition: &api.ExpressionArg{Dtype: "exp", Data: &api.ExpressionArg_Exp{Exp: &api.Expression{
Name: "hasStudyStatus",
Data: []*api.ExpressionArg{
{Dtype: "str", Data: &api.ExpressionArg_Str{Str: "active"}},
},
}}},
})
ok, msg := shouldHaveGrpcErrorStatus(err, "no participant found")
if !ok {
t.Error(msg)
}
})

t.Run("with hardcoded condition", func(t *testing.T) {
_, err := s.HasParticipantStateWithCondition(context.Background(), &api.ProfilesWithConditionReq{
ProfileIds: []string{testProfileID1},
StudyKey: testStudies[0].Key,
InstanceId: testInstanceID,
Condition: &api.ExpressionArg{Dtype: "num", Data: &api.ExpressionArg_Num{Num: 1}},
})
if err != nil {
t.Errorf("unexpected error: %s", err.Error())
}
})

t.Run("with profiles fulfilling condition", func(t *testing.T) {
_, err := s.HasParticipantStateWithCondition(context.Background(), &api.ProfilesWithConditionReq{
ProfileIds: []string{testProfileID1},
StudyKey: testStudies[0].Key,
InstanceId: testInstanceID,
Condition: &api.ExpressionArg{Dtype: "exp", Data: &api.ExpressionArg_Exp{Exp: &api.Expression{
Name: "lastSubmissionDateOlderThan",
Data: []*api.ExpressionArg{
{Dtype: "num", Data: &api.ExpressionArg_Num{Num: 10}},
{Dtype: "str", Data: &api.ExpressionArg_Str{Str: "s3"}},
},
}}},
})
if err != nil {
t.Errorf("unexpected error: %s", err.Error())
}

_, err = s.HasParticipantStateWithCondition(context.Background(), &api.ProfilesWithConditionReq{
ProfileIds: []string{testProfileID2},
StudyKey: testStudies[0].Key,
InstanceId: testInstanceID,
Condition: &api.ExpressionArg{Dtype: "exp", Data: &api.ExpressionArg_Exp{Exp: &api.Expression{
Name: "hasStudyStatus",
Data: []*api.ExpressionArg{
{Dtype: "str", Data: &api.ExpressionArg_Str{Str: "exited"}},
},
}}},
})
if err != nil {
t.Errorf("unexpected error: %s", err.Error())
return
}
})
}

0 comments on commit 651afb3

Please sign in to comment.