public function testCRUD_Works() { $environ = new MongoTestEnvironment(); $environ->clean(); $project = $environ->createProject(SF_TESTPROJECT, SF_TESTPROJECTCODE); // List $list = new TextListModel($project); $list->read(); $this->assertEquals(0, $list->count); // Create $text = new TextModel($project); $text->title = "Some Text"; $usx = MongoTestEnvironment::usxSample(); $text->content = $usx; $id = $text->write(); $this->assertNotNull($id); $this->assertInternalType('string', $id); $this->assertEquals($text->id->asString(), $id); // Read back $otherText = new TextModel($project, $id); $this->assertEquals($id, $otherText->id->asString()); $this->assertEquals('Some Text', $otherText->title); $this->assertEquals($usx, $otherText->content); // Update $otherText->title = 'Other Text'; $otherText->write(); // Read back $otherText = new TextModel($project, $id); $this->assertEquals('Other Text', $otherText->title); // List $list->read(); $this->assertEquals(1, $list->count); // Delete TextModel::remove($project->databaseName(), $id); // List $list->read(); $this->assertEquals(0, $list->count); }
/** * @param string $projectId * @param string $userId * @returns array - the DTO array */ public static function encode($projectId, $userId) { $userModel = new UserModel($userId); $projectModel = new SfchecksProjectModel($projectId); $textList = new TextListModel($projectModel); $textList->read(); $list = $projectModel->listUsers(); $data = array(); $data['count'] = count($list->entries); $data['entries'] = array_values($list->entries); // re-index array $data['project'] = ProjectSettingsDtoEncoder::encode($projectModel); unset($data['project']['users']); $data['archivedTexts'] = array(); foreach ($textList->entries as $entry) { $textModel = new TextModel($projectModel, $entry['id']); if ($textModel->isArchived) { $questionList = $textModel->listQuestionsWithAnswers(); // Just want count of questions and responses, not whole list $entry['questionCount'] = $questionList->count; $responseCount = 0; // "Responses" = answers + comments foreach ($questionList->entries as $q) { foreach ($q['answers'] as $a) { $commentCount = count($a['comments']); $responseCount += $commentCount + 1; // +1 for this answer } } $entry['responseCount'] = $responseCount; $entry['dateModified'] = $textModel->dateModified->asDateTimeInterface()->format(\DateTime::RFC2822); $data['archivedTexts'][] = $entry; } } $data['rights'] = RightsHelper::encode($userModel, $projectModel); $data['bcs'] = BreadCrumbHelper::encode('settings', $projectModel, null, null); return $data; }
/** * @param string $projectId * @param string $userId * @returns array - the DTO array * @throws ResourceNotAvailableException */ public static function encode($projectId, $userId) { $user = new UserModel($userId); $project = new SfchecksProjectModel($projectId); if ($project->isArchived && $project->users[$userId]->role != ProjectRoles::MANAGER) { throw new ResourceNotAvailableException("This Project is no longer available. If this is incorrect contact your project manager."); } $textList = new TextListModel($project); $textList->read(); $data = array(); $data['rights'] = RightsHelper::encode($user, $project); $data['project'] = array('name' => $project->projectName, 'id' => $projectId); if ($project->isArchived) { $data['project']['name'] .= " [ARCHIVED]"; } $data['texts'] = array(); foreach ($textList->entries as $entry) { $text = new TextModel($project, $entry['id']); if (!$text->isArchived) { $questionList = $text->listQuestionsWithAnswers(); // Just want count of questions and responses, not whole list $entry['questionCount'] = 0; $responseCount = 0; // "Responses" = answers + comments foreach ($questionList->entries as $q) { $question = new QuestionModel($project, $q['id']); if (!$question->isArchived) { $entry['questionCount']++; foreach ($q['answers'] as $a) { $commentCount = count($a['comments']); $responseCount += $commentCount + 1; // +1 for this answer } } } $entry['responseCount'] = $responseCount; $entry['dateCreated'] = $text->dateCreated->asDateTimeInterface()->format(\DateTime::RFC2822); $data['texts'][] = $entry; } } // sort Texts with newest at the top usort($data['texts'], function ($a, $b) { $sortOn = 'dateCreated'; if (array_key_exists($sortOn, $a) && array_key_exists($sortOn, $b)) { return strtotime($a[$sortOn]) < strtotime($b[$sortOn]) ? 1 : -1; } else { return 0; } }); // future support for members $data['members'] = array(); // unread activity count $unreadActivity = new UnreadActivityModel($userId, $projectId); $unreadItems = $unreadActivity->unreadItems(); $data['activityUnreadCount'] = count($unreadItems); // unread broadcast messages $unreadMessages = new UnreadMessageModel($userId, $projectId); $messageIds = $unreadMessages->unreadItems(); $messages = array(); foreach ($messageIds as $messageId) { $message = new MessageModel($project, $messageId); $messages[] = array('id' => $message->id->asString(), 'subject' => $message->subject, 'content' => $message->content); } $data['broadcastMessages'] = $messages; return $data; }
public static function UserEngagementReport($projectId) { $project = ProjectModel::getById($projectId); $output = str_pad('**** User Engagement Report ****', 120, " ", STR_PAD_BOTH) . "\n"; $output .= str_pad(date(DATE_RFC2822), 120, " ", STR_PAD_BOTH) . "\n\n"; $data = array(); $activeUsers = array(); $managerUsers = array(); $inactiveUsers = array(); $invalidUsers = array(); $listModel = new UserListProjectModel($projectId); $listModel->read(); if ($listModel->count > 0) { $textListModel = new TextListModel($project); $textListModel->read(); $questions = array(); foreach ($textListModel->entries as $text) { $questionListModel = new QuestionAnswersListModel($project, $text['id']); $questionListModel->read(); $questions = array_merge($questions, array_map(function ($q) use($text) { $q['textRef'] = $text['id']; return $q; }, $questionListModel->entries)); } $answerCtr = 0; $commentCtr = 0; foreach ($listModel->entries as $user) { $userModel = new UserModel($user['id']); $user['isActive'] = $userModel->active; $user['questions'] = 0; $user['texts'] = 0; $user['answers'] = 0; $user['comments'] = 0; $user['responses'] = 0; $user['textIds'] = array(); if (!$user['isActive']) { if (!$user['email']) { $user['email'] = $userModel->emailPending; } array_push($invalidUsers, $user); continue; } if ($project->users->offsetExists($user['id'])) { $user['role'] = $project->users[$user['id']]->role; } else { $user['role'] = ProjectRoles::NONE; } $answerCtr = 0; $commentCtr = 0; foreach ($questions as $question) { $responses = 0; foreach ($question['answers'] as $answer) { if (!$answer['content']) { continue; } $answerCtr++; foreach ($answer['comments'] as $comment) { if (!$comment['content']) { continue; } $commentCtr++; if ($comment['userRef'] && strval($comment['userRef']) == $user['id']) { $user['comments']++; $user['responses']++; array_push($user['textIds'], $question['textRef']); $responses++; } } if ($answer['userRef'] && strval($answer['userRef']) == $user['id']) { $user['answers']++; $user['responses']++; array_push($user['textIds'], $question['textRef']); $responses++; } } if ($responses > 0) { $user['questions']++; } } $user['texts'] = count(array_unique($user['textIds'])); if ($user['role'] == ProjectRoles::MANAGER) { array_push($managerUsers, $user); } elseif ($user['responses'] > 0) { array_push($activeUsers, $user); } else { array_push($inactiveUsers, $user); } } $output .= $project->projectName . " Project\n"; $output .= "Texts (T's) in Project: " . $textListModel->count . "\n"; $output .= "Questions (Q's) in Project: " . count($questions) . "\n"; $output .= "Responses (R's) in Project (Answers + Comments): " . ($answerCtr + $commentCtr) . "\n"; $output .= "Answers (A's) in Project: " . $answerCtr . "\n"; $output .= "Comments (C's) in Project: " . $commentCtr . "\n"; } else { $output .= "This project has no users\n\n"; } $sortByResponses = function ($a, $b) { if ($a['responses'] > $b['responses']) { return -1; } elseif ($a['responses'] < $b['responses']) { return 1; } else { if ($a['answers'] > $b['answers']) { return -1; } elseif ($a['answers'] < $b['answers']) { return 1; } else { if ($a['comments'] > $b['comments']) { return -1; } elseif ($a['comments'] < $b['comments']) { return 1; } else { return strcmp($a['username'], $b['username']); } } } }; $sortByName = function ($a, $b) { return strcasecmp($a['name'], $b['name']); }; usort($activeUsers, $sortByResponses); usort($managerUsers, $sortByResponses); usort($inactiveUsers, $sortByName); usort($invalidUsers, $sortByName); $output .= "\n\nManagers: " . count($managerUsers) . "\n" . str_pad("Name", 30) . str_pad("Email", 35) . str_pad("Username", 25) . str_pad("R's", 5) . str_pad("A's", 5) . str_pad("C's", 5) . str_pad("Q's", 5) . str_pad("T's", 5) . "\n\n"; foreach ($managerUsers as $user) { $output .= str_pad($user['name'], 30) . str_pad($user['email'], 35) . str_pad($user['username'], 25) . str_pad($user['responses'], 5) . str_pad($user['answers'], 5) . str_pad($user['comments'], 5) . str_pad($user['questions'], 5) . str_pad($user['texts'], 5) . "\n"; } $output .= "\n\nActive Users: " . count($activeUsers) . "\n" . str_pad("Name", 30) . str_pad("Email", 35) . str_pad("Username", 25) . str_pad("R's", 5) . str_pad("A's", 5) . str_pad("C's", 5) . str_pad("Q's", 5) . str_pad("T's", 5) . "\n\n"; foreach ($activeUsers as $user) { $output .= str_pad($user['name'], 30) . str_pad($user['email'], 35) . str_pad($user['username'], 25) . str_pad($user['responses'], 5) . str_pad($user['answers'], 5) . str_pad($user['comments'], 5) . str_pad($user['questions'], 5) . str_pad($user['texts'], 5) . "\n"; } $output .= "\n\nInactive Users (never engaged): " . count($inactiveUsers) . "\n" . str_pad("Name", 30) . str_pad("Email", 35) . str_pad("Username", 25) . "\n\n"; foreach ($inactiveUsers as $user) { $output .= str_pad($user['name'], 30) . str_pad($user['email'], 35) . str_pad($user['username'], 25) . "\n"; } $output .= "\n\nInvited Users (but never validated or logged in): " . count($invalidUsers) . "\n" . str_pad("Name", 30) . str_pad("Email", 35) . str_pad("Username", 25) . "\n\n"; foreach ($invalidUsers as $user) { $output .= str_pad($user['name'], 30) . str_pad($user['email'], 35) . str_pad($user['username'], 25) . "\n"; } $data['output'] = $output; $data['result'] = array('managerUsers' => $managerUsers, 'activeUsers' => $activeUsers, 'inactiveUsers' => $inactiveUsers, 'invitedUsers' => $invalidUsers); return $data; }
public function run($mode = 'test') { $testMode = $mode == 'test'; $message = ""; $userList = new UserListModel(); $userList->read(); $userIds = array_map(function ($e) { return $e['id']; }, $userList->entries); $projectList = new ProjectListModel(); $projectList->read(); $projectIds = array_map(function ($e) { return $e['id']; }, $projectList->entries); $deadCommentUserRefs = 0; $deadAnswerUserRefs = 0; foreach ($projectIds as $projectId) { $project = new ProjectModel($projectId); $textList = new TextListModel($project); $textList->read(); $textIds = array_map(function ($e) { return $e['id']; }, $textList->entries); foreach ($textIds as $textId) { $questionList = new QuestionListModel($project, $textId); $questionList->read(); $questionIds = array_map(function ($e) { return $e['id']; }, $questionList->entries); foreach ($questionIds as $questionId) { $question = new QuestionModel($project, $questionId); foreach ($question->answers as $answerId => $answer) { foreach ($answer->comments as $commentId => $comment) { /** @var IdReference $ref */ $ref = $comment->userRef; if (!empty($ref->id) && !in_array($ref->asString(), $userIds)) { $comment->userRef->id = ''; if (!$testMode) { $question->writeComment($project->databaseName(), $questionId, $answerId, $comment); } $deadCommentUserRefs++; $message .= "Removed dead user-comment ref {$ref} from question {$questionId}, answer {$answerId}, comment {$commentId}\n"; } } $ref = $answer->userRef; if (!empty($ref->id) && !in_array($ref->asString(), $userIds)) { $answer->userRef->id = ''; if (!$testMode) { $question->writeAnswer($answer); } $deadAnswerUserRefs++; $message .= "Removed dead user-answer ref {$ref} from question {$questionId}, answer {$answerId}\n"; } } } } } if ($deadAnswerUserRefs > 0) { $message .= "\n\nRemoved dead user references from {$deadAnswerUserRefs} answers\n\n"; } else { $message .= "\n\nNo dead user references were found in answers\n\n"; } if ($deadCommentUserRefs > 0) { $message .= "\n\nRemoved dead user references from {$deadCommentUserRefs} comments\n\n"; } else { $message .= "\n\nNo dead user references were found in comments\n\n"; } return $message; }
public function run($mode = 'test') { $testMode = $mode != 'run'; $message = ""; $projectlist = new ProjectListModel(); $projectlist->read(); $textsExamined = 0; $textsUpdated = 0; // foreach existing project foreach ($projectlist->entries as $projectParams) { $projectId = $projectParams['id']; if ($projectParams['projectName'] == 'Jamaican Psalms') { $project = new SfchecksProjectModel($projectId); $textlist = new TextListModel($project); $textlist->read(); // foreach text in project foreach ($textlist->entries as $textParams) { $textsExamined++; $textId = $textParams['id']; $legacyText = new TextModel_sf_v0_9_18($project, $textId); $fileName = ''; if ($legacyText->audioUrl) { $text = new TextModel($project, $textId); if (!$testMode) { if (!$text->audioFileName) { // legacy audioUrl format "assets/<projectId>/<textId>_<fileName>" $fileNamePrefix = $textId . '_'; $pos = strpos($legacyText->audioUrl, $fileNamePrefix); $text->audioFileName = substr($legacyText->audioUrl, $pos + strlen($fileNamePrefix)); } $text->write(); } $message .= "Changed text: {$text->title}\n"; $textsUpdated++; } } if (!$testMode) { TextModel_sf_v0_9_18::removeAudioProperty($project->databaseName()); $message .= "Removed 'audioUrl' property from project: {$project->projectName}\n"; } } } if ($textsUpdated > 0) { $message .= "\n\nChanged {$textsUpdated} legacy texts to only store audio filename\n\n"; } else { $message .= "\n\nNo legacy text audio were found/changed. {$textsExamined} texts examined.\n\n"; } // re-arrange assets folder // - remove <siteName> from path for LF // - add <appName> to path for SF and change <projectId> in path to <projectSlug (databaseName)> $message .= "\n\nRe-arrange assets folder\n~~~~~~~~~~~~~~~~~~~~~~~~\n\n"; $project = new SfchecksProjectModel(); $assetsFolderPath = APPPATH . "assets"; $assetsSubfolders = glob($assetsFolderPath . '/*'); @mkdir($assetsFolderPath . '/lexicon'); @mkdir($assetsFolderPath . '/sfchecks'); foreach ($assetsSubfolders as $assetsSubfolder) { if (file_exists($assetsSubfolder) && is_dir($assetsSubfolder)) { $assetsSubfolderName = basename($assetsSubfolder); if (strpos($assetsSubfolderName, 'languageforge') !== false) { $message .= "Move into lexicon: {$assetsSubfolderName}\n"; $oldFolderPath = $assetsSubfolder . '/lexicon'; $newFolderPath = $assetsFolderPath . '/lexicon'; } elseif ($assetsSubfolderName == 'lexicon' || $assetsSubfolderName == 'sfchecks') { $message .= "No change: {$assetsSubfolderName}\n"; $oldFolderPath = ''; $newFolderPath = ''; $assetsSubfolder = ''; } elseif ($project->exists($assetsSubfolderName)) { $message .= "Move into sfchecks: {$assetsSubfolderName}\n"; $oldFolderPath = $assetsSubfolder; $project->read($assetsSubfolderName); $projectSlug = $project->databaseName(); $newFolderPath = $assetsFolderPath . "/sfchecks/{$projectSlug}"; if (!$testMode) { } } else { $message .= "Delete: {$assetsSubfolderName}\n"; $oldFolderPath = ''; $newFolderPath = ''; } if (!$testMode) { if (file_exists($oldFolderPath) && is_dir($oldFolderPath)) { if (!@rename($oldFolderPath, $newFolderPath)) { $oldFiles = glob($oldFolderPath . '/*'); foreach ($oldFiles as $oldFile) { $newFile = $newFolderPath . '/' . basename($oldFile); rename($oldFile, $newFile); } } } if (file_exists($assetsSubfolder) && is_dir($assetsSubfolder)) { $this->recursiveRemoveFolder($assetsSubfolder); } } } } return $message; }