@@ -23,10 +23,13 @@ import { StorageEngine } from '../../storage/engines/types';
2323import { useStorageEngine } from '../../storage/storageEngineHooks' ;
2424import { FirebaseStorageEngine } from '../../storage/engines/FirebaseStorageEngine' ;
2525import { useAsync } from '../../store/hooks/useAsync' ;
26+ import { useAuth } from '../../store/hooks/useAuth' ;
2627import { getCleanedDuration } from '../../utils/getCleanedDuration' ;
2728import { showNotification } from '../../utils/notifications' ;
2829import { studyComponentToIndividualComponent } from '../../utils/handleComponentInheritance' ;
2930import { parseConditionParam } from '../../utils/handleConditionLogic' ;
31+ import { getAnswerIdentifier , getParticipantQualitativeCodes } from './qualitativeCodes' ;
32+ import type { DownloadedQualitativeCodes } from './qualitativeCodes' ;
3033
3134const OPTIONAL_COMMON_PROPS = [
3235 'condition' ,
@@ -50,6 +53,8 @@ const OPTIONAL_COMMON_PROPS = [
5053 'responseMax' ,
5154 'configHash' ,
5255 'metaData' ,
56+ 'participantTags' ,
57+ 'taskTags' ,
5358] as const ;
5459
5560const REQUIRED_PROPS = [
@@ -105,23 +110,25 @@ function participantDataToRows(
105110 properties : Property [ ] ,
106111 studyConfig ?: StudyConfig ,
107112 transcripts ?: Record < string , string | null > ,
113+ qualitativeCodes ?: DownloadedQualitativeCodes ,
108114) : [ TidyRow [ ] , string [ ] ] {
109115 const percentComplete = ( ( Object . entries ( participant . answers ) . filter ( ( [ _ , entry ] ) => entry . endTime !== - 1 ) . length / ( Object . entries ( participant . answers ) . length ) ) * 100 ) . toFixed ( 2 ) ;
110116 const newHeaders = new Set < string > ( ) ;
111117 const participantConditions = parseConditionParam ( participant . conditions ?? participant . searchParams ?. condition ) ;
112118 const conditionValue = participantConditions . length > 0 ? participantConditions . join ( ',' ) : 'default' ;
113119 const metaData = JSON . stringify ( participant . metadata ) ;
120+ const participantQualitativeTags = JSON . stringify ( qualitativeCodes ?. participantTags ?? [ ] ) ;
114121
115122 return [ [
116- {
123+ ... ( properties . includes ( 'participantTags' ) ? [ {
117124 participantId : participant . participantId ,
118125 trialId : 'participantTags' ,
119126 trialOrder : null ,
120127 responseId : 'participantTags' ,
121- answer : JSON . stringify ( participant . participantTags ) ,
128+ answer : participantQualitativeTags ,
122129 ...( properties . includes ( 'condition' ) ? { condition : conditionValue } : { } ) ,
123130 ...( properties . includes ( 'stage' ) ? { stage : participant . stage } : { } ) ,
124- } ,
131+ } ] : [ ] ) ,
125132 ...( properties . includes ( 'metaData' ) ? [ {
126133 participantId : participant . participantId ,
127134 trialId : 'metaData' ,
@@ -137,6 +144,8 @@ function participantDataToRows(
137144 const { trialOrder } = trialAnswer ;
138145 const trialConfig = studyConfig ?. components ?. [ trialId ] ;
139146 const completeComponent = trialConfig && studyConfig ? studyComponentToIndividualComponent ( trialConfig , studyConfig ) : undefined ;
147+ const identifier = getAnswerIdentifier ( trialAnswer ) ;
148+ const taskQualitativeTags = JSON . stringify ( qualitativeCodes ?. taskTags [ identifier ] ?? [ ] ) ;
140149
141150 const duration = trialAnswer . endTime === - 1 ? undefined : trialAnswer . endTime - trialAnswer . startTime ;
142151 const cleanedDuration = getCleanedDuration ( trialAnswer ) ;
@@ -189,8 +198,10 @@ function participantDataToRows(
189198 if ( properties . includes ( 'answer' ) ) {
190199 tidyRow . answer = typeof value === 'object' ? JSON . stringify ( value ) : value ;
191200 }
201+ if ( properties . includes ( 'taskTags' ) ) {
202+ tidyRow . taskTags = taskQualitativeTags ;
203+ }
192204 if ( properties . includes ( 'transcript' ) ) {
193- const identifier = trialAnswer . identifier || `${ trialId } _${ trialOrder } ` ;
194205 tidyRow . transcript = transcripts ?. [ `${ participant . participantId } _${ identifier } ` ] ?? undefined ;
195206 }
196207 if ( properties . includes ( 'correctAnswer' ) ) {
@@ -261,6 +272,7 @@ export async function getTableData(
261272 storageEngine : StorageEngine | undefined ,
262273 studyId : string ,
263274 hasAudio ?: boolean ,
275+ authEmail = 'temp' ,
264276) {
265277 if ( ! storageEngine ) {
266278 return { header : [ ] , rows : [ ] , missingConfigCount : 0 } ;
@@ -280,7 +292,7 @@ export async function getTableData(
280292 . filter ( ( answer ) => ( ( answer ?. endTime ?? - 1 ) > 0 ) || ( ( answer ?. startTime ?? - 1 ) > 0 ) )
281293 . map ( ( answer ) => ( { answer, participantId : p . participantId } ) ) ) ;
282294 const tasks = allAnswers . map ( ( { answer, participantId } ) => async ( ) => {
283- const identifier = answer . identifier || ` ${ answer . componentName } _ ${ answer . trialOrder } ` ;
295+ const identifier = getAnswerIdentifier ( answer ) ;
284296 const key = `${ participantId } _${ identifier } ` ;
285297
286298 try {
@@ -300,14 +312,19 @@ export async function getTableData(
300312 } ) ;
301313 const header = combinedProperties
302314 . filter ( ( p ) => p !== 'condition' || hasCondition )
303- . filter ( ( p ) => p !== 'metaData' ) ;
315+ . filter ( ( p ) => p !== 'metaData' )
316+ . filter ( ( p ) => p !== 'participantTags' ) ;
304317 const allData = await Promise . all ( data . map ( async ( participant ) => {
305318 const participantConfig = allConfigs [ participant . participantConfigHash ] ;
319+ const qualitativeCodes = selectedProperties . includes ( 'participantTags' ) || selectedProperties . includes ( 'taskTags' )
320+ ? await getParticipantQualitativeCodes ( storageEngine , authEmail , participant )
321+ : undefined ;
306322 const partDataToRows = await participantDataToRows (
307323 participant ,
308324 combinedProperties ,
309325 hasStoredStudyConfig ( participantConfig ) ? participantConfig : undefined ,
310326 transcripts ,
327+ qualitativeCodes ,
311328 ) ;
312329
313330 return partDataToRows ;
@@ -351,6 +368,7 @@ export function DownloadTidy({
351368 const [ isDownloading , setIsDownloading ] = useState ( false ) ;
352369 const [ downloadProgress , setDownloadProgress ] = useState ( 0 ) ;
353370 const [ showTranscriptWarning , setShowTranscriptWarning ] = useState ( false ) ;
371+ const auth = useAuth ( ) ;
354372
355373 const [ selectedProperties , setSelectedProperties ] = useState < Array < OptionalProperty > > ( [
356374 'condition' ,
@@ -369,7 +387,7 @@ export function DownloadTidy({
369387 ] ) ;
370388
371389 const { storageEngine } = useStorageEngine ( ) ;
372- const { value : tableData , status : tableDataStatus , error : tableError } = useAsync ( getTableData , [ selectedProperties , data , storageEngine , studyId , hasAudio ] ) ;
390+ const { value : tableData , status : tableDataStatus , error : tableError } = useAsync ( getTableData , [ selectedProperties , data , storageEngine , studyId , hasAudio , auth . user . user ?. email || 'temp' ] ) ;
373391 const isFirebase = storageEngine ?. getEngine ( ) === 'firebase' ;
374392 const transcriptAvailable = isFirebase && ! ! hasAudio ;
375393 const selectedParticipantCount = data . length ;
0 commit comments