Skip to content

Commit

Permalink
Merge pull request #4 from susom/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
Jordan-M-Schultz authored Nov 27, 2024
2 parents 112d293 + 61c677d commit 8ed59fd
Show file tree
Hide file tree
Showing 8 changed files with 376 additions and 107 deletions.
61 changes: 58 additions & 3 deletions IntakeDashboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Exception;
use REDCap;
use Project;
use Survey;


class IntakeDashboard extends AbstractExternalModule
Expand Down Expand Up @@ -43,6 +44,7 @@ public function injectJSMO($data = null, $init_method = null): void
*/
public function generateAssetFiles(): array
{
$this->fetchRequiredSurveys(1);
$cwd = $this->getModulePath();
$assets = [];

Expand Down Expand Up @@ -105,6 +107,19 @@ public function redcap_module_ajax($action, $payload, $project_id, $record, $ins
};
}

/**
* @param $project_id
* @param $record
* @param $instrument
* @param $event_id
* @param $group_id
* @param $survey_hash
* @param $response_id
* @param $repeat_instance
* @return void
* @throws Exception
* Function that runs on each survey completion
*/
public function redcap_survey_complete(
$project_id,
$record = null,
Expand Down Expand Up @@ -261,6 +276,7 @@ public function generateREDCapEventName($proj, $event_id): string

/**
* Fetches intake participation data for the current user.
* Rendered on the intake dashboard view
*
* @return string|false JSON-encoded string of intake participation data, or false on error.
*/
Expand Down Expand Up @@ -308,6 +324,8 @@ public function fetchIntakeParticipation(): string|false
foreach ($userIntakes as &$intake) {
foreach ($intakeDetails as $detail) {
if ($detail['record_id'] === $intake['intake_id']) {
$survey_id = $proj->forms[$pSettings['universal-survey-form-immutable']]['survey_id'];
$intake['completion_timestamp'] = Survey::isResponseCompleted($survey_id, $detail['record_id'], $pSettings['universal-survey-event'], 1, true);
$intake['intake_complete'] = $detail['intake_complete'] ?? null;
$intake['pi_name'] = trim($detail['pi_f_name'] . " " . $detail['pi_l_name']);;
$intake['research_title'] = $detail['research_title'] ?? null;
Expand Down Expand Up @@ -372,16 +390,53 @@ public function checkUserDetailAccess($payload): string
}


public function fetchRequiredSurveys()
public function fetchRequiredSurveys($id)
{
try {
// $settings = $this->getSystemSettings();
$child_ids = $this->getSystemSetting('project-id');
$parent_id = $this->getSystemSetting('parent-project');

$projectSettings = $this->getProjectSettings($parent_id);
if (empty($parent_id) || empty($child_ids))
throw new \Exception("Parent project or child projects have not been configured properly, exiting");
$universal_survey_form_name = $this->getProjectSetting('universal-survey', $parent_id);
$universal_survey_form_name = $this->getProjectSetting('universal-survey-form-immutable', $parent_id);

$detailsParams = [
"return_format" => "json",
"project_id" => $parent_id,
"records" => $id
];

$completedIntake = json_decode(REDCap::getData($detailsParams), true);
$requiredChildPIDs = [];

foreach (reset($completedIntake) as $key => $value) {
// Check if the key matches the pattern "serv_map_" where x is an integer
if (preg_match('/^serv_map_\d+$/', $key) && $value === "1") {
$key = array_search($key, $projectSettings['mapping-field']);

if($key !== false) { //can return 0 index
$requiredChildPIDs[] = $projectSettings['project-id'][$key];
}
}
}

// Iterate through each child project and generate a record for a survey
foreach($requiredChildPIDs as $value) {
$a = new \Project($value);
$surveyInfo = $a->surveys;
$saveData = [
[
"record_id" => 1,
"abc" => ""
]
];

// Save data using REDCap's saveData function
$a = REDCap::saveData($value, 'json', json_encode($saveData), 'overwrite');
$test = Survey::getSurveyLinkFromParticipantId(1);
$abd = 1;
}

$a = new \Project($parent_id);
$surveyInfo = $a->surveys;
Expand Down
11 changes: 9 additions & 2 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,16 @@
"key": "project-id",
"name": "<b>Project</b>",
"type": "project-id",
"required": true,
"repeatable": true
"required": true
},
{
"key": "mapping-field",
"name": "<b>Universal intake mapping field</b>",
"type": "field-list",
"required": true
}


]
}

Expand Down
12 changes: 6 additions & 6 deletions dashboard-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion dashboard-ui/src/assets/react.svg

This file was deleted.

16 changes: 16 additions & 0 deletions dashboard-ui/src/components/AppHeader/appHeader.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import { Group, Image } from '@mantine/core';

export function AppHeader() {
return (
<Group h="100%" px="md">
<Image
src="https://storage.googleapis.com/group-chat-therapy/stanford-logo.svg"
h={30}
w="auto"
fit="contain"
alt="stanford_image"
/>
</Group>
);
}
4 changes: 4 additions & 0 deletions dashboard-ui/src/views/Dashboard/dashboard.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.mantine-Blockquote-icon {
background-color: rgb(248, 249, 250);
}

.mantine-Blockquote-root {
padding: 20px;
}
62 changes: 40 additions & 22 deletions dashboard-ui/src/views/Dashboard/dashboard.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, {useState, useEffect, useCallback} from "react";
import {AppShell, Button, Group, Card, Image, Table, Text, Divider, Title, Blockquote, List, Loader} from '@mantine/core';
import {AppShell, Button, Group, Card, Image, Table, Text, Divider, Pagination, Title, Blockquote, List, Loader} from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { IconInfoCircle } from '@tabler/icons-react';
import {useNavigate} from "react-router-dom";
import { AppHeader } from '../../components/AppHeader/appHeader'; // Import the reusable header
import './dashboard.css';

export function Dashboard() {
Expand All @@ -17,15 +18,40 @@ export function Dashboard() {
}, [])

const fetchIntakes = () => {
let jsmoModule;
if(import.meta?.env?.MODE !== 'development')
jsmoModule = ExternalModules.Stanford.IntakeDashboard
jsmoModule.fetchIntakeParticipation(successCallback, errorCallback)
const useFakeData = false; // Set this to false to fetch real data

if (useFakeData) {
// Fake data for testing, remove this block when using real data
toggle();
setIntakes([
{
intake_id: "12345",
type: "Survey",
research_title: "Sample Study 1",
pi_name: "Dr. John Doe",
intake_complete: "Incomplete",
},
{
intake_id: "67890",
type: "Interview",
research_title: "Sample Study 2",
pi_name: "Dr. Jane Smith",
intake_complete: "Complete",
},
]);
} else {
// Real data fetching
let jsmoModule;
if (import.meta?.env?.MODE !== 'development')
jsmoModule = ExternalModules.Stanford.IntakeDashboard;
jsmoModule.fetchIntakeParticipation(successCallback, errorCallback);
}
};

}

const successCallback = (res) => {
console.log('success', res)
toggle()
setIntakes(res?.data)
}

Expand Down Expand Up @@ -53,14 +79,14 @@ export function Dashboard() {

const tableData = {
caption: 'List of intakes you have been added to',
head: ['UID', 'Listed Contact Type', 'Study Title', 'PI Name', 'Completion Status', 'Detail'],
body: (intakes && intakes.length > 0) ? intakes.map(item => [item.intake_id, item.type, item.research_title, item.pi_name, item.intake_complete, renderNavButton(item.intake_id)]) : []
head: ['UID', 'Initial submission date', 'Study Title', 'PI Name', 'Status', 'Detail'],
body: (intakes && intakes.length > 0) ? intakes.map(item => [item.intake_id, item.completion_timestamp, item.research_title, item.pi_name, item.intake_complete, renderNavButton(item.intake_id)]) : []
}

const finishedTable = {
caption: 'List of completed intakes',
head: ['UID', 'Listed Contact Type', 'Study Title', 'PI Name', 'Completion Status', 'Detail'],
body: (intakes && intakes.length > 0) ? intakes.map(item => [item.intake_id, item.type, item.research_title, item.pi_name, item.intake_complete, renderNavButton(item.intake_id)]) : []
head: ['UID', 'Initial submission date', 'Study Title', 'PI Name', 'Status', 'Detail'],
body: (intakes && intakes.length > 0) ? intakes.map(item => [item.intake_id, item.completion_timestamp, item.research_title, item.pi_name, item.intake_complete, renderNavButton(item.intake_id)]) : []
}

return (
Expand All @@ -69,21 +95,13 @@ export function Dashboard() {
padding="md"
>
<AppShell.Header>
<Group h="100%" px="md">
<Image src="https://storage.googleapis.com/group-chat-therapy/stanford-logo.svg"
h={30}
w="auto"
fit="contain"
alt="stanford_image"
/>

</Group>
<AppHeader/>
</AppShell.Header>
<AppShell.Main style={{backgroundColor: 'rgb(248,249,250)'}}>
<Title order={3}>Welcome to your intake dashboard</Title>
<Text c="dimmed">Logged in as: {globalUsername}</Text>
<Blockquote color="blue" iconSize={36} mt="xl" radius="md" icon={<IconInfoCircle/>}>
<List size="md">
<Blockquote color="blue" iconSize={36} mt="lg" radius="md" icon={<IconInfoCircle/>}>
<List size="sm">
<List.Item>These tables represent active and completed research intakes affiliated with your username</List.Item>
<List.Item>You will see an entry in either table for any submissions that list your username as a contact</List.Item>
<List.Item>Missing a submission? Please contact your PI</List.Item>
Expand All @@ -105,7 +123,7 @@ export function Dashboard() {
</Card.Section>
</Card>

<div style={{marginTop: '100px'}}></div>
<div style={{marginTop: '60px'}}></div>

<Card withBorder shadow="sm" radius="md">
<Card.Section withBorder inheritPadding py="xs">
Expand Down
Loading

0 comments on commit 8ed59fd

Please sign in to comment.