Repository: NextCenturyCorporation/itm-evaluation-dashboard
Pull request #270: Itm 915
Author: login: dereknop
Created: 2025-03-26T16:51:19Z | Merged: 2025-03-27T20:03:52Z

[Ticket](https://nextcentury.atlassian.net/jira/software/projects/ITM/boards/116?assignee=60ba425da547eb00686ee0ce&selectedIssue=ITM-915)

Run [ingest](https://github.com/NextCenturyCorporation/itm-ingest/pull/119) first 

http://localhost:3000/results
http://localhost:3000/adm-results
http://localhost:3000/adm-probe-responses

Should all be functional now. Multi-kdma should not be an option for /adm-results. You'll want to grab the newest backup of the db and also need to rebuild gql container. 


Diff:
diff --git a/dashboard-ui/src/components/AdmCharts/admCharts.jsx b/dashboard-ui/src/components/AdmCharts/admCharts.jsx
index a7e00465..cc6b8834 100644
--- a/dashboard-ui/src/components/AdmCharts/admCharts.jsx
+++ b/dashboard-ui/src/components/AdmCharts/admCharts.jsx
@@ -15,16 +15,18 @@ class AdmCharts extends React.Component {
 
     constructor(props) {
         super();
-        props.evaluationOptions.sort((a, b) => (a.value < b.value) ? 1 : -1)
 
+        const filteredOptions = props.evaluationOptions.filter(option => option.value !== 7);
+        filteredOptions.sort((a, b) => (a.value < b.value) ? 1 : -1);
+        this.filteredOptions = filteredOptions
         this.state = {
-            currentEval: props.evaluationOptions[0],
+            currentEval: filteredOptions[0],
         }
 
         this.selectEvaluation = this.selectEvaluation.bind(this);
     }
 
-    selectEvaluation(target){
+    selectEvaluation(target) {
         this.setState({
             currentEval: target
         });
@@ -32,24 +34,24 @@ class AdmCharts extends React.Component {
 
     getHeaderLabel(id, name) {
         if (this.state.currentEval.value < 3) {
-            if(id.toLowerCase().indexOf("adept") > -1 ) {
+            if (id.toLowerCase().indexOf("adept") > -1) {
                 return ("BBN: " + id + " " + name);
             } else {
                 return ("Soartech: " + id + " " + name);
             }
         } else if (this.state.currentEval.value == 3) {
-            if(id.toLowerCase().indexOf("metricseval") > -1 ) {
+            if (id.toLowerCase().indexOf("metricseval") > -1) {
                 return ("ADEPT: " + id + " " + name);
             } else {
                 return ("Soartech: " + name);
             }
         } else {
-            if(id.toLowerCase().includes("qol") || id.toLowerCase().includes("vol") ) {
+            if (id.toLowerCase().includes("qol") || id.toLowerCase().includes("vol")) {
                 return ("Soartech: " + name + " (" + id + ")");
             } else {
                 return ("Adept: " + name + " (" + id + ")");
             }
-        
+
         }
     }
 
@@ -62,7 +64,7 @@ class AdmCharts extends React.Component {
                         <div className="evaluation-selector-holder">
                             <Select
                                 onChange={this.selectEvaluation}
-                                options={this.props.evaluationOptions}
+                                options={this.filteredOptions}
                                 defaultValue={this.state.currentEval}
                             />
                         </div>
@@ -70,43 +72,43 @@ class AdmCharts extends React.Component {
                 </div>
                 {this.state.currentEval !== '' &&
                     <Query query={scenario_names_aggregation} variables={{ "evalNumber": this.state?.currentEval?.value }}>
-                    {
-                        ({ loading, error, data }) => {
-                            if (loading) return <div>Loading ...</div> 
-                            if (error) return <div>Error</div>
+                        {
+                            ({ loading, error, data }) => {
+                                if (loading) return <div>Loading ...</div>
+                                if (error) return <div>Error</div>
 
-                            const scenarioNameOptions = data[getScenarioNamesQueryName];
-                            let scenariosArray = [];
-                            for(let i=0; i < scenarioNameOptions.length; i++) {
-                                scenariosArray.push({
-                                    "id": scenarioNameOptions[i]._id.id, 
-                                    "name": scenarioNameOptions[i]._id.name
-                                });
-                            }
-                            scenariosArray.sort((a, b) => (a.id > b.id) ? 1 : -1);
+                                const scenarioNameOptions = data[getScenarioNamesQueryName];
+                                let scenariosArray = [];
+                                for (let i = 0; i < scenarioNameOptions.length; i++) {
+                                    scenariosArray.push({
+                                        "id": scenarioNameOptions[i]._id.id,
+                                        "name": scenarioNameOptions[i]._id.name
+                                    });
+                                }
+                                scenariosArray.sort((a, b) => (a.id > b.id) ? 1 : -1);
 
-                            return (
-                                <div className="home-container">
-                                    {
-                                        scenariosArray.map(scenario =>
-                                            <div className='chart-home-container' key={"id_" +scenario.id}>
-                                                <div className='chart-header'>
-                                                    <div className='chart-header-label'>
-                                                        <h4>{this.getHeaderLabel(scenario.id, scenario.name)}</h4>
+                                return (
+                                    <div className="home-container">
+                                        {
+                                            scenariosArray.map(scenario =>
+                                                <div className='chart-home-container' key={"id_" + scenario.id}>
+                                                    <div className='chart-header'>
+                                                        <div className='chart-header-label'>
+                                                            <h4>{this.getHeaderLabel(scenario.id, scenario.name)}</h4>
+                                                        </div>
                                                     </div>
+                                                    <ScoreChart testid={scenario.id} evalNumber={this.state.currentEval.value} />
                                                 </div>
-                                                <ScoreChart testid={scenario.id} evalNumber={this.state.currentEval.value}/>
-                                            </div>
-                                        )
-                                    }
-                                </div>
-                            )
+                                            )
+                                        }
+                                    </div>
+                                )
+                            }
                         }
-                    }
                     </Query>
                 }
             </div>
-            
+
         );
     }
 }
diff --git a/dashboard-ui/src/components/AdmCharts/admProbeResponses.jsx b/dashboard-ui/src/components/AdmCharts/admProbeResponses.jsx
index eeccefc5..6d6c7b73 100644
--- a/dashboard-ui/src/components/AdmCharts/admProbeResponses.jsx
+++ b/dashboard-ui/src/components/AdmCharts/admProbeResponses.jsx
@@ -7,6 +7,7 @@ import * as XLSX from 'sheetjs-style';
 import { saveAs } from 'file-saver';
 import '../../css/results-page.css';
 import '../../css/aggregateResults.css';
+import { multiSort } from '../Results/resultsTable';
 
 const get_eval_name_numbers = gql`
     query getEvalNameNumbers {
@@ -21,11 +22,9 @@ const scenario_names_aggregation = gql`
 `;
 
 const performer_adm_by_scenario = gql`
-    query getPerformerADMsForScenario($admQueryStr: String, $scenarioID: ID){
-        getPerformerADMsForScenario(admQueryStr: $admQueryStr, scenarioID: $scenarioID)
-    }
-`;
-
+    query getPerformerADMsForScenario($admQueryStr: String, $scenarioID: ID, $evalNumber: Float){
+        getPerformerADMsForScenario(admQueryStr: $admQueryStr, scenarioID: $scenarioID, evalNumber: $evalNumber)
+    }`;
 const alignment_target_by_scenario = gql`
     query getAlignmentTargetsPerScenario($evalNumber: Float!, $scenarioID: ID) {
         getAlignmentTargetsPerScenario(evalNumber: $evalNumber, scenarioID: $scenarioID)
@@ -144,19 +143,21 @@ export const ADMProbeResponses = (props) => {
         variables: { evalNumber: currentEval },
         skip: !currentEval
     });
+
     const { loading: alignmentLoading, error: alignmentError, data: alignmentData } = useQuery(alignment_target_by_scenario, {
         variables: { evalNumber: currentEval, scenarioID: currentScenario },
         skip: !currentScenario
     });
     const { loading: admLoading, error: admError, data: admData } = useQuery(performer_adm_by_scenario, {
-        variables: { admQueryStr: queryString, scenarioID: currentScenario },
+        variables: { admQueryStr: queryString, scenarioID: currentScenario, evalNumber: currentEval },
         skip: !currentScenario,
         fetchPolicy: "network-only"
     });
 
     useEffect(() => {
         if (alignmentData?.getAlignmentTargetsPerScenario) {
-            setAlignmentTargets(alignmentData.getAlignmentTargetsPerScenario);
+            const sortedTargets = [...alignmentData.getAlignmentTargetsPerScenario].sort(multiSort);
+            setAlignmentTargets(sortedTargets);
         }
     }, [alignmentData]);
 
diff --git a/dashboard-ui/src/components/AdmCharts/scoreChart.jsx b/dashboard-ui/src/components/AdmCharts/scoreChart.jsx
index 9cb7e2cc..4af3a44e 100644
--- a/dashboard-ui/src/components/AdmCharts/scoreChart.jsx
+++ b/dashboard-ui/src/components/AdmCharts/scoreChart.jsx
@@ -68,15 +68,15 @@ class ScoreChart extends React.Component {
                     if (error) return <div>Error</div>
 
                     const chartData = data[GET_HISTORIES_BY_ID]?.filter((x) => x.evalNumber == this.props.evalNumber);
-
-                    let admNameParamter = "ADM Name";
+                    
+                    let admNameParameter = "ADM Name";
                     if(this.props.evalNumber > 2) {
-                        admNameParamter = "adm_name";
+                        admNameParameter = "adm_name";
                     }
 
                     let newScores = {};
                     chartData.map((item) => {
-                        const performer = item.history[0].parameters[admNameParamter];
+                        const performer = item.history[0].parameters[admNameParameter];
                         const prevPerformerData = newScores[performer] || { total: 0, count: 0 };
                         newScores[performer] = {
                             total: prevPerformerData.total + item.history[item.history.length - 1].response.score,
diff --git a/dashboard-ui/src/components/Results/alignmentScore.jsx b/dashboard-ui/src/components/Results/alignmentScore.jsx
index 761394d1..98f19d3e 100644
--- a/dashboard-ui/src/components/Results/alignmentScore.jsx
+++ b/dashboard-ui/src/components/Results/alignmentScore.jsx
@@ -3,17 +3,18 @@ import Box from '@material-ui/core/Box';
 import Typography from '@material-ui/core/Typography';
 
 const AlignmentScoreBox = ({ performer, data, scenario }) => {
-  let score = "Not Available"
+  let score = null
   let sessionId = null;
 
-  if(data.history !== null && data.history !== undefined) {
-    if( data.history[data.history.length - 1].response.score !== undefined) {
-        score = data.history[data.history.length - 1].response.score.toFixed(4);
+  if (data.history !== null && data.history !== undefined) {
+    const lastHistoryItem = data.history[data.history.length - 1];
+    if (lastHistoryItem?.response?.score) {
+      score = lastHistoryItem.response.score.toFixed(4);
     }
 
-    for(let i=0; i < data.history.length - 1; i++) {
-      if(data.history[i].command === "TA1 Alignment Target Session ID") {
-        sessionId = data.history[i].response; 
+    for (let i = 0; i < data.history.length - 1; i++) {
+      if (data.history[i].command === "TA1 Alignment Target Session ID") {
+        sessionId = data.history[i].response;
       }
     }
   }
@@ -22,14 +23,22 @@ const AlignmentScoreBox = ({ performer, data, scenario }) => {
     <Box className="scoreBox">
       <Typography variant="h5">
         {performer}
-        <br />
-      </Typography>
-      <Typography variant="h5">
-        Alignment Score: {score}
-        {scenario === 'kickoff-demo-scenario-1' && 
-          <iframe src={"http://localhost:8084/graph/session/" + sessionId} title="SoarGraph" width="800px" height="800px"></iframe>
-        }
       </Typography>
+      
+      {score !== null && (
+        <Typography variant="h5">
+          Alignment Score: {score}
+        </Typography>
+      )}
+      
+      {scenario === 'kickoff-demo-scenario-1' && sessionId && (
+        <iframe 
+          src={`http://localhost:8084/graph/session/${sessionId}`} 
+          title="SoarGraph" 
+          width="800px" 
+          height="800px"
+        />
+      )}
     </Box>
   );
 }
diff --git a/dashboard-ui/src/components/Results/resultsTable.jsx b/dashboard-ui/src/components/Results/resultsTable.jsx
index 9b69f7fc..ee359dfb 100644
--- a/dashboard-ui/src/components/Results/resultsTable.jsx
+++ b/dashboard-ui/src/components/Results/resultsTable.jsx
@@ -19,7 +19,7 @@ import AlignmentScoreBox from './alignmentScore';
 import '../../css/results-page.css';
 import { Query } from 'react-apollo';
 import { RQ2223 } from '../Research/tables/rq22-rq23';
-
+import { Phase2_RQ23 } from '../Research/tables/rq23_ph2'
 
 const getScenarioNamesQueryName = "getScenarioNamesByEval";
 const getPerformerADMByScenarioName = "getPerformerADMsForScenario";
@@ -37,18 +37,31 @@ const scenario_names_aggregation = gql`
         getScenarioNamesByEval(evalNumber: $evalNumber)
     }`;
 const performer_adm_by_scenario = gql`
-    query getPerformerADMsForScenario($admQueryStr: String, $scenarioID: ID){
-        getPerformerADMsForScenario(admQueryStr: $admQueryStr, scenarioID: $scenarioID)
+    query getPerformerADMsForScenario($admQueryStr: String, $scenarioID: ID, $evalNumber: Float){
+        getPerformerADMsForScenario(admQueryStr: $admQueryStr, scenarioID: $scenarioID, evalNumber: $evalNumber)
     }`;
 const test_by_adm_and_scenario = gql`
     query getTestByADMandScenario($admQueryStr: String, $scenarioID: ID, $admName: ID, $alignmentTarget: String, $evalNumber: Int){
         getTestByADMandScenario(admQueryStr: $admQueryStr, scenarioID: $scenarioID, admName: $admName, alignmentTarget: $alignmentTarget, evalNumber: $evalNumber)
     }`;
 const alignment_target_by_scenario = gql`
-    query getAlignmentTargetsPerScenario($evalNumber: Float!, $scenarioID: ID){
-        getAlignmentTargetsPerScenario(evalNumber: $evalNumber, scenarioID: $scenarioID)
+    query getAlignmentTargetsPerScenario($evalNumber: Float!, $scenarioID: ID, $admName: ID){
+        getAlignmentTargetsPerScenario(evalNumber: $evalNumber, scenarioID: $scenarioID, admName: $admName)
     }`;
 
+export const multiSort = (a, b) => {
+    const aMatch = a.match(/^([a-zA-Z]+)(\d+)$/);
+    const bMatch = b.match(/^([a-zA-Z]+)(\d+)$/);
+
+    // only if same base string
+    if (aMatch && bMatch && aMatch[1] === bMatch[1]) {
+        return parseInt(aMatch[2], 10) - parseInt(bMatch[2], 10);
+    }
+
+    // if different base string just use alph.
+    return a.localeCompare(b);
+};
+
 
 class ResultsTable extends React.Component {
 
@@ -57,7 +70,7 @@ class ResultsTable extends React.Component {
         this.state = {
             adm: "",
             scenario: "",
-            evalNumber: 5,
+            evalNumber: 7,
             ADMQueryString: "history.parameters.adm_name",
             showScrollButton: false,
             alignmentTarget: null
@@ -136,7 +149,7 @@ class ResultsTable extends React.Component {
                 return ("Soartech: " + id);
             }
         } else {
-            if(id.toLowerCase().includes("qol") || id.toLowerCase().includes("vol") ) {
+            if (id.toLowerCase().includes("qol") || id.toLowerCase().includes("vol")) {
                 return ("Soartech: " + id);
             } else {
                 return ("Adept: " + id);
@@ -240,7 +253,7 @@ class ResultsTable extends React.Component {
                                     <span className="nav-header-text">Performer/ADM</span>
                                 </div>
                                 <div className="nav-menu">
-                                    <Query query={performer_adm_by_scenario} variables={{ "admQueryStr": this.state.ADMQueryString, "scenarioID": this.state.scenario }}>
+                                    <Query query={performer_adm_by_scenario} variables={{ "admQueryStr": this.state.ADMQueryString, "scenarioID": this.state.scenario, "evalNumber": this.state.evalNumber }}>
                                         {
                                             ({ loading, error, data }) => {
                                                 if (loading) return <div>Loading ...</div>
@@ -274,42 +287,43 @@ class ResultsTable extends React.Component {
                                 </div>
                             </>
                         }
-                        {(this.state.evalNumber >= 3 && this.state.scenario !== ""  && this.state.adm !== "")&&
+                        {(this.state.evalNumber >= 3 && this.state.scenario !== "" && this.state.adm !== "") &&
                             <>
                                 <div className="nav-header">
                                     <span className="nav-header-text">Alignment Target</span>
                                 </div>
                                 <div className="nav-menu">
-                                <Query query={alignment_target_by_scenario} variables={{"evalNumber": this.state.evalNumber, "scenarioID": this.state.scenario}}>
-                                    {
-                                        ({ loading, error, data }) => {
-                                            if (loading) return <div>Loading ...</div> 
-                                            if (error) return <div>Error</div>
-
-                                            const alignmentTargetOptions = data[getAlignmentTargetsPerScenario];
-                                            let alignmentTargetArray = [];
-                                            for(const element of alignmentTargetOptions) {
-                                                alignmentTargetArray.push({
-                                                    "value": element,
-                                                    "name": element
-                                                });
+                                    <Query query={alignment_target_by_scenario} variables={{ "evalNumber": this.state.evalNumber, "scenarioID": this.state.scenario, "admName": this.state.adm }}>
+                                        {
+                                            ({ loading, error, data }) => {
+                                                if (loading) return <div>Loading ...</div>
+                                                if (error) return <div>Error</div>
+
+                                                const alignmentTargetOptions = data[getAlignmentTargetsPerScenario];
+                                                let alignmentTargetArray = [];
+                                                for (const element of alignmentTargetOptions) {
+                                                    alignmentTargetArray.push({
+                                                        "value": element,
+                                                        "name": element
+                                                    });
+                                                }
+
+                                                alignmentTargetArray.sort((a, b) => multiSort(a.value, b.value))
+
+                                                return (
+                                                    <List className="nav-list" component="nav" aria-label="secondary mailbox folder">
+                                                        {alignmentTargetArray.map((item, key) =>
+                                                            <ListItem className="nav-list-item" id={"alignTarget_" + key} key={"alignTarget_" + key}
+                                                                button
+                                                                selected={this.state.alignmentTarget === item.value}
+                                                                onClick={() => this.setAlignmentTarget(item.value)}>
+                                                                <ListItemText primary={item.value} />
+                                                            </ListItem>
+                                                        )}
+                                                    </List>
+                                                )
                                             }
-                                            alignmentTargetArray.sort((a, b) => (a.value > b.value) ? 1 : -1);
-
-                                            return (
-                                                <List className="nav-list" component="nav" aria-label="secondary mailbox folder">
-                                                    {alignmentTargetArray.map((item,key) =>
-                                                        <ListItem className="nav-list-item" id={"alignTarget_" + key} key={"alignTarget_" + key}
-                                                            button
-                                                            selected={this.state.alignmentTarget === item.value}
-                                                            onClick={() => this.setAlignmentTarget(item.value)}>
-                                                            <ListItemText primary={item.value} />
-                                                        </ListItem>
-                                                    )}
-                                                </List>
-                                            )
                                         }
-                                    }
                                     </Query>
                                 </div>
                             </>
@@ -318,10 +332,10 @@ class ResultsTable extends React.Component {
                     <div className="test-overview-area">
                         {((this.state.evalNumber < 3 && this.state.scenario !== "" && this.state.adm !== "") || (
                             this.state.evalNumber >= 3 && this.state.scenario !== "" && this.state.adm !== "" && this.state.alignmentTarget !== null)) ?
-                            <Query query={test_by_adm_and_scenario} variables={{"admQueryStr": this.state.ADMQueryString, "scenarioID": this.state.scenario, "admName": this.state.adm, "alignmentTarget": this.state.alignmentTarget, "evalNumber": this.state.evalNumber}}>
+                            <Query query={test_by_adm_and_scenario} variables={{ "admQueryStr": this.state.ADMQueryString, "scenarioID": this.state.scenario, "admName": this.state.adm, "alignmentTarget": this.state.alignmentTarget, "evalNumber": this.state.evalNumber }}>
                                 {
                                     ({ loading, error, data }) => {
-                                        if (loading) return <div>Loading ...</div> 
+                                        if (loading) return <div>Loading ...</div>
                                         if (error) return <div>Error</div>
                                         const testData = data[getTestByADMandScenarioName];
                                         return (
@@ -348,8 +362,11 @@ class ResultsTable extends React.Component {
                                 }
                             </Query> :
                             <>
-                                {this.state.evalNumber >= 4 ?
-                                    <RQ2223 evalNum={this.state.evalNumber} /> :
+                                {(this.state.evalNumber >= 4) ?
+                                    (this.state.evalNumber >= 7) ?
+                                        <Phase2_RQ23/> :
+                                        <RQ2223 evalNum={this.state.evalNumber} />
+                                    :
                                     <div className="graph-section">
                                         <h2>Please select a{this.state.scenario == "" ? " scenario" : this.state.adm == "" ? "n ADM" : "n alignment target"} to view results</h2>
                                     </div>}
@@ -366,13 +383,13 @@ class ResultsTable extends React.Component {
                         left: '20px',
                         bottom: '20px',
                         borderRadius: '10px',
-                        backgroundColor: '#592610', 
-                        color: 'white', 
+                        backgroundColor: '#592610',
+                        color: 'white',
                         cursor: 'pointer',
-                        zIndex: 1000, 
-                        boxShadow: '0px 2px 10px rgba(0,0,0,0.3)' 
+                        zIndex: 1000,
+                        boxShadow: '0px 2px 10px rgba(0,0,0,0.3)'
                     }}>
-                        Back To Top <ArrowUpwardIcon fontSize='large'/>
+                        Back To Top <ArrowUpwardIcon fontSize='large' />
                     </IconButton>
                 )}
             </div>
@@ -450,19 +467,19 @@ function ActionRow({ item }) {
         <React.Fragment>
             <TableRow className='noBorderRow' onClick={() => setOpen(!open)}>
                 <TableCell className="noBorderCell tableCellIcon">
-                <IconButton
+                    <IconButton
                         aria-label="expand row"
                         onClick={(e) => {
                             e.stopPropagation();
                             setOpen(!open);
                         }}
                     >
-                        {open ? <KeyboardArrowUpIcon fontSize='large'/> : <KeyboardArrowDownIcon fontSize='large'/>}
+                        {open ? <KeyboardArrowUpIcon fontSize='large' /> : <KeyboardArrowDownIcon fontSize='large' />}
                     </IconButton>
                 </TableCell>
                 <TableCell className="noBorderCell tableCellCommand">
                     <Typography><strong>Command:</strong> {item.command}</Typography>
-                    <Typography>Parameters: {!(Object.keys(item.parameters).length > 0) ? "None" : ""}</Typography>  
+                    <Typography>Parameters: {!(Object.keys(item.parameters).length > 0) ? "None" : ""}</Typography>
                     {renderNestedItems(item.parameters, item.command == 'Take Action' ? item.response : null)}
                 </TableCell>
             </TableRow>
diff --git a/node-graphql/server.schema.js b/node-graphql/server.schema.js
index b282ea4f..05bec31b 100644
--- a/node-graphql/server.schema.js
+++ b/node-graphql/server.schema.js
@@ -166,8 +166,8 @@ const typeDefs = gql`
     getScenario(scenarioId: ID): JSON
     getScenarioNames: [JSON]
     getScenarioNamesByEval(evalNumber: Float): [JSON]
-    getPerformerADMsForScenario(admQueryStr: String, scenarioID: ID): JSON,
-    getAlignmentTargetsPerScenario(evalNumber: Float, scenarioID: ID): JSON,
+    getPerformerADMsForScenario(admQueryStr: String, scenarioID: ID, evalNumber: Float): JSON,
+    getAlignmentTargetsPerScenario(evalNumber: Float, scenarioID: ID, admName: ID): JSON,
     getTestByADMandScenario(admQueryStr: String, scenarioID: ID, admName: ID, alignmentTarget: String, evalNumber: Int): JSON
     getAllTestDataForADM(admQueryStr: String, scenarioID: ID, admName: ID, alignmentTargets: [String], evalNumber: Int): [JSON]
     getAllScenarios(id: ID): [Scenario]
@@ -325,13 +325,36 @@ const resolvers = {
         .toArray().then(result => { return result });
     },
     getPerformerADMsForScenario: async (obj, args, context, inflow) => {
-      return await context.db.collection('admTargetRuns').distinct(args["admQueryStr"], { "history.response.id": args["scenarioID"] }).then(result => { return result });
+      const query = { "scenario": args["scenarioID"] };
+      
+      if (args["evalNumber"] !== undefined && args["evalNumber"] !== null) {
+        query["evalNumber"] = args["evalNumber"];
+      }
+      
+      return await context.db.collection('admTargetRuns')
+        .distinct(args["admQueryStr"], query)
+        .then(result => { return result });
     },
     getAlignmentTargetsPerScenario: async (obj, args, context, inflow) => {
-      let alignmentTargets = await context.db.collection('admTargetRuns').distinct("history.response.id", { "history.response.id": args["scenarioID"], "evalNumber": args["evalNumber"] }).then(result => { return result });
+      const query = { 
+        "scenario": args["scenarioID"], 
+        "evalNumber": args["evalNumber"] 
+      };
+      
+      if (args["admName"] !== undefined && args["admName"] !== null) {
+        query["adm_name"] = args["admName"];
+      }
+      
+      let alignmentTargets = await context.db.collection('admTargetRuns')
+        .distinct("history.response.id", query)
+        .then(result => { return result });
+      
       // The scenarioID still comes back in this distinct because of the way the JSON is configured, remove it before sending the array
       const indexOfScenario = alignmentTargets.indexOf(args["scenarioID"]);
-      alignmentTargets.splice(indexOfScenario, 1);
+      if (indexOfScenario >= 0) {
+        alignmentTargets.splice(indexOfScenario, 1);
+      }
+      
       return alignmentTargets;
     },
     getTestByADMandScenario: async (obj, args, context, inflow) => {


Reviews:
user: login: nextcen-dgemoets
body: This all looks great.

The only thing that made me pause is how the "synthetic" alignment target ids (e.g., `target1` through `target498`, the rows of the csv) are in the dashboard.  I just generated those names because they needed to be unique, and traced back to the csv file, but they're not very informative in the dashboard.  I wonder if there's a way to use the PID from the csv to identify from which human that synthetic alignment target is derived?

In retrospect, maybe I could have named these alignment targets `[pid]-[eval]-[type]`, e.g., `target1` would become `20249201-4-overall`.  That would require updating the experimental server branch that Kitware is using right now...maybe it's too late.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T17:33:37Z
user: login: kaitlyn-sharo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T19:56:28Z
user: login: kaitlyn-sharo
body: I think sorting the targets differently (target1, target2, target3 rather than target 1, target 10, target 100) would be really nice. However this did take away eval 7 where it wasn't supposed to be and made the data visible on the other two pages. If you think the target sorting is a different ticket, happy to approve.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T20:22:58Z
user: login: kaitlyn-sharo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T14:28:00Z
user: login: kaitlyn-sharo
body: Target numbers are organized prettier now! I think everything works as intended
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T15:18:56Z
user: login: kaitlyn-sharo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T19:18:45Z
user: login: nextcen-dgemoets
body: LGTM.  As far as I know, this is everything we need here.  Maybe when Jennifer looks at the data next week she'll have insight as to whether KW and AD will be able to look at the table and have it be easy to see what is going on and what the values are.

It strikes me that in the ADM pages, it's called "Multi-kdma Experiment" but in RQ2, it's called "Phase II Experiment 1".  But that's not a function of this ticket.
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T20:00:23Z

Review comments:
diff_hunk: @@ -348,7 +348,7 @@ class ResultsTable extends React.Component {
                                 }
                             </Query> :
                             <>
-                                {this.state.evalNumber >= 4 ?
+                                {(this.state.evalNumber >= 4) && (this.state.evalNumber != 7) ?
path: dashboard-ui/src/components/Results/resultsTable.jsx
user: login: kaitlyn-sharo
body: This should show the new table from my PR if evalNumber==7
created_at: 2025-03-26T19:56:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -68,15 +68,17 @@ class ScoreChart extends React.Component {
                     if (error) return <div>Error</div>
 
                     const chartData = data[GET_HISTORIES_BY_ID]?.filter((x) => x.evalNumber == this.props.evalNumber);
+                    
 
-                    let admNameParamter = "ADM Name";
+                    console.log(chartData)
path: dashboard-ui/src/components/AdmCharts/scoreChart.jsx
user: login: kaitlyn-sharo
body: remove console log
created_at: 2025-03-27T14:28:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -348,7 +348,7 @@ class ResultsTable extends React.Component {
                                 }
                             </Query> :
                             <>
-                                {this.state.evalNumber >= 4 ?
+                                {(this.state.evalNumber >= 4) && (this.state.evalNumber != 7) ?
path: dashboard-ui/src/components/Results/resultsTable.jsx
user: login: kaitlyn-sharo
body: This can be done now
created_at: 2025-03-27T19:18:45Z
author_association: CONTRIBUTOR

PR comments:
user: login: dereknop
created_at: 2025-03-26T20:55:54Z
body: > I think sorting the targets differently (target1, target2, target3 rather than target 1, target 10, target 100) would be really nice. 

Frustrating that the built in sort function doesn't do this on its own. Should be good now. 

author_association: CONTRIBUTOR
user: login: nextcen-dgemoets
created_at: 2025-03-27T15:22:36Z
body: > The only thing that made me pause is how the "synthetic" alignment target ids (e.g., `target1` through `target498`, the rows of the csv) are in the dashboard. I just generated those names because they needed to be unique, and traced back to the csv file, but they're not very informative in the dashboard. I wonder if there's a way to use the PID from the csv to identify from which human that synthetic alignment target is derived?
> 
> In retrospect, maybe I could have named these alignment targets `[pid]-[eval]-[type]`, e.g., `target1` would become `20249201-4-overall`. That would require updating the experimental server branch that Kitware is using right now...maybe it's too late.

@brianpippin  Do you or Jennifer want to comment on the target IDs appearing as `target1` through `target498` in the dashboard?  Is there anything we should do about that, either for this ticket or a subsequent one?  If not, then this ticket is good to go.
author_association: CONTRIBUTOR
user: login: brianpippin
created_at: 2025-03-27T18:33:33Z
body: > > The only thing that made me pause is how the "synthetic" alignment target ids (e.g., `target1` through `target498`, the rows of the csv) are in the dashboard. I just generated those names because they needed to be unique, and traced back to the csv file, but they're not very informative in the dashboard. I wonder if there's a way to use the PID from the csv to identify from which human that synthetic alignment target is derived?
> > In retrospect, maybe I could have named these alignment targets `[pid]-[eval]-[type]`, e.g., `target1` would become `20249201-4-overall`. That would require updating the experimental server branch that Kitware is using right now...maybe it's too late.
> 
> @brianpippin Do you or Jennifer want to comment on the target IDs appearing as `target1` through `target498` in the dashboard? Is there anything we should do about that, either for this ticket or a subsequent one? If not, then this ticket is good to go.

Is there anything that maps "targetN" to the values used for that target?
author_association: CONTRIBUTOR
user: login: nextcen-dgemoets
created_at: 2025-03-27T18:41:41Z
body: > Is there anything that maps "targetN" to the values used for that target?

It's the order of the rows in the csv file, which appears to be the order the ADEPT scenarios (at least those with all values filled in) are returned from the `userScenarioResults` table.  Perhaps a one-off script can update the alignment target IDs in those tables based on this?  I can't imagine we want to update the server branch to set the target IDs this way-- unless of course we envision Kitware wanting to re-run things.
author_association: CONTRIBUTOR
user: login: brianpippin
created_at: 2025-03-27T18:45:33Z
body: > > Is there anything that maps "targetN" to the values used for that target?
> 
> It's the order of the rows in the csv file, which appears to be the order the ADEPT scenarios (at least those with all values filled in) are returned from the `userScenarioResults` table. Perhaps a one-off script can update the alignment target IDs in those tables based on this? I can't imagine we want to update the server branch to set the target IDs this way-- unless of course we envision Kitware wanting to re-run things.

I am asking because if ADEPT or KITWARE look at the table I want it to be easy to see what is going on and what the values are, even if there is a key for the values or something.
author_association: CONTRIBUTOR
user: login: nextcen-dgemoets
created_at: 2025-03-27T19:34:15Z
body: > > > Is there anything that maps "targetN" to the values used for that target?
> > 
> > It's the order of the rows in the csv file, which appears to be the order the ADEPT scenarios (at least those with all values filled in) are returned from the `userScenarioResults` table. Perhaps a one-off script can update the alignment target IDs in those tables based on this? I can't imagine we want to update the server branch to set the target IDs this way-- unless of course we envision Kitware wanting to re-run things.
> 
> I am asking because if ADEPT or KITWARE look at the table I want it to be easy to see what is going on and what the values are, even if there is a key for the values or something.

I really don't know if this will be sufficient for Adept and Kitware.  I feel like maybe they'll need a little more, but I'm not sure exactly what.  I guess we can just hand it over to them and let them ask for something, but I was trying to get ahead of it if there was something we knew made sense to give them.  Or maybe when Jennifer looks at all of this next week, she'll ask for something to supplement what we have now in this table or the RQ2 table.
author_association: CONTRIBUTOR