9#include "tpcclibConfig.h"
27 RES *reslist,
int resNr,
ResVOI *lastResVOI,
int *resIndex);
32static char *info[] = {
33 "Collects the model analysis results from a set of PET studies, and",
34 "by default tabulates regional results and means with standard deviations;",
35 "then results of only one model analysis program can be used at a time.",
37 "Usage: @P [Options] collection_file result_files",
41 " Results will be sorted by study number.",
43 " Quits with error when normally a warning would be displayed.",
45 " Regional results are not collected but result files are just",
46 " tabulated into one HTML file.",
48 " Regional and individual results are collected for each parameter.",
52 " rescoll patlak_means.html ua4???ki.res",
54 "The collected data is saved in tables in XHTML format, therefore the",
55 "collection filename extension has to be .htm(l).",
56 "File can then be viewed and printed with a web browser or Excel.",
57 "In Excel the data can be also be processed further and saved in Excel",
60 "See also: reslist, pardiff, dft2res, fit2res, resmatch, resdel, paradd",
62 "Keywords: results, tabulation, reporting, modelling, simulation, tool",
81int main(
int argc,
char **argv)
83 int ai, help=0, version=0, verbose=1;
84 int ri, rj, vi, pi, ret, len;
86 int doSort=0, beStrict=0;
92 char colfile[FILENAME_MAX], resfile[FILENAME_MAX], *cptr, tmp[1024];
101 if(argc==1) {
tpcPrintUsage(argv[0], info, stderr);
return(1);}
102 colfile[0]=resfile[0]=(char)0;
105 for(ai=1; ai<argc; ai++)
if(*argv[ai]==
'-') {
107 cptr=argv[ai]+1;
if(*cptr==
'-') cptr++;
if(cptr==NULL)
continue;
108 if(strcasecmp(cptr,
"STRICT")==0) {
109 beStrict=1;
continue;
110 }
else if(strncasecmp(cptr,
"SORT", 2)==0) {
112 }
else if(strncasecmp(cptr,
"LIST", 2)==0) {
114 }
else if(strncasecmp(cptr,
"PARAMETER", 3)==0) {
117 fprintf(stderr,
"Error: invalid option '%s'.\n", argv[ai]);
122 if(help==2) {
tpcHtmlUsage(argv[0], info,
"");
return(0);}
127 for(; ai<argc; ai++) {
129 strlcpy(colfile, argv[ai], FILENAME_MAX);
131 cptr=strrchr(colfile,
'.');
132 if(strcasecmp(cptr,
".HTM")!=0 && strcasecmp(cptr,
".HTML")!=0) {
133 fprintf(stderr,
"Error: collection file extension must be .htm or .html\n");
139 if(access(argv[ai], 0) == -1) {
140 fprintf(stderr,
"Error: result file %s does not exist.\n", argv[ai]);
143 if(first_res<0) first_res=ai;
149 fprintf(stderr,
"Error: no result files to process.\n");
151 }
else if(resNr<2 && doMode!=1) {
152 fprintf(stderr,
"Error: at least two result files must be given.\n");
158 printf(
"colfile := %s\n", colfile);
159 printf(
"resNr := %d\n", resNr);
160 printf(
"doSort := %d\n", doSort);
161 printf(
"doMode := %d\n", doMode);
162 printf(
"saveSD := %d\n", saveSD);
163 printf(
"first_res := %d\n", first_res);
171 if(verbose>1) printf(
"reading result files\n");
173 res=(
RES*)calloc(resNr,
sizeof(
RES));
174 if(res==NULL) {fprintf(stderr,
"Error: out of memory.\n");
return(2);}
176 for(ri=0; ri<resNr; ri++)
resInit(res+ri);
178 for(ai=first_res, vi=0; ai<argc; ai++) {
179 strcpy(resfile, argv[ai]);
180 if(verbose>0) fprintf(stdout,
"file[%d] := %s\n", vi+1, resfile);
182 ret=
resRead(resfile, res+vi, verbose-3);
185 fprintf(stderr,
"Warning: '%s' not read: %s\n", resfile,
reserrmsg);
188 fprintf(stderr,
"Error in reading '%s': %s\n", resfile,
reserrmsg);
189 for(ri=0; ri<vi; ri++)
resEmpty(res+ri);
193 if(verbose>2) printf(
"res.voiNr := %d\n", res[vi].voiNr);
195 for(
int i=0; i<res[vi].
voiNr; i++) {
205 fprintf(stderr,
"Error: %s contains duplicate region names.\n", resfile);
206 for(ri=0; ri<vi; ri++)
resEmpty(res+ri);
210 if(strlen(res[vi].studynr)<1 || strcmp(res[vi].studynr,
".")==0) {
211 cptr=strrchr(resfile,
'/');
if(cptr==NULL) cptr=strrchr(resfile,
'\\');
212 if(cptr==NULL) cptr=resfile;
else cptr++;
214 cptr=strrchr(res[vi].studynr,
'.');
if(cptr!=NULL) *cptr=(char)0;
219 if(verbose>0) fflush(stdout);
222 fprintf(stderr,
"Error: no result file(s) could be read.\n");
223 for(ri=0; ri<resNr; ri++)
resEmpty(res+ri);
225 }
else if(resNr<2 && doMode!=1) {
226 fprintf(stderr,
"Error: only one result file could be read.\n");
227 for(ri=0; ri<resNr; ri++)
resEmpty(res+ri);
230 if(verbose>0) printf(
"%d result files read.\n", resNr);
231 if(verbose>20)
for(ri=0; ri<resNr; ri++)
resPrint(res+ri);
234 if(verbose>1) printf(
"sorting results by study number\n");
235 for(ri=0; ri<resNr-1; ri++)
for(rj=ri+1; rj<resNr; rj++)
236 if(strcmp(res[ri].studynr, res[rj].studynr)>0) {
237 memcpy(&tres, res+ri,
sizeof(
RES));
238 memcpy(res+ri, res+rj,
sizeof(
RES));
239 memcpy(res+rj, &tres,
sizeof(
RES));
242 if(verbose>19)
for(ri=0; ri<resNr; ri++)
resPrint(res+ri);
245 for(ri=0; ri<resNr; ri++)
246 printf(
"study_number[%d] := %s\n", ri+1, res[ri].studynr);
254 if(verbose>1) printf(
"checking that results can be combined\n");
255 strcpy(tmp, res[0].program); cptr=strtok(tmp,
" \t\n\r(");
256 if(cptr==NULL) len=strlen(res[0].program);
else len=strlen(cptr);
257 for(ri=1; ri<resNr; ri++) {
258 if(strncasecmp(res[0].program, res[ri].program, len)==0)
continue;
259 fprintf(stderr,
"Error: different software used in result files.\n");
260 for(ri=0; ri<resNr; ri++)
resEmpty(res+ri);
263 for(ri=1, ret=0; ri<resNr; ri++) {
265 if(res[ri].parNr!=res[0].parNr) {
266 fprintf(stderr,
"Error: different parameter nr in result files.\n");
271 fprintf(stderr,
"Error: different parameters in result files.\n");
276 for(ri=0; ri<resNr; ri++)
resEmpty(res+ri);
287 fprintf(stderr,
"Error: too many result files; %d allowed.\n",
MAX_RESPARAMS);
288 for(ri=0; ri<resNr; ri++)
resEmpty(res+ri);
299 for(ri=1; ri<resNr; ri++) {
300 if(res[0].voiNr!=res[ri].voiNr) {ret=1;
break;}
304 if(ret==1) fprintf(stderr,
"Error: in this mode region nr must match.\n");
305 else fprintf(stderr,
"Error: in this mode regions must match.\n");
306 for(ri=0; ri<resNr; ri++)
resEmpty(res+ri);
311 if(verbose>0) printf(
"collecting results...\n");
316 if(verbose>1) printf(
"writing %s\n", colfile);
322 if((fp=fopen(colfile,
"w"))==NULL) {
323 fprintf(stderr,
"Error: cannot open file '%s' for write.\n", colfile);
324 for(ri=0; ri<resNr; ri++)
resEmpty(res+ri);
331 fprintf(stderr,
"Error: cannot write in file '%s'.\n", colfile);
332 for(ri=0; ri<resNr; ri++)
resEmpty(res+ri);
333 fclose(fp); remove(colfile);
return(11);
337 fprintf(fp,
"\n<body>\n");
340 fprintf(fp,
"\n<div id=\"title\">\n");
341 strcpy(tmp, res[0].program); cptr=strtok(tmp,
" \t\n\r(");
342 if(cptr==NULL) cptr=res[0].
program;
343 if(doMode!=1 && strlen(cptr)>1)
344 fprintf(fp,
" <h1>Results of %s</h1>\n", cptr);
346 fprintf(fp,
" <h1>Results</h1>\n");
347 fprintf(fp,
"</div>\n");
354 fprintf(fp,
"\n<div id=\"regcontainer\">\n");
355 fprintf(fp,
"<ul id=\"reglist\">\n");
358 fprintf(stderr,
"Error: cannot get result file contents.\n");
359 for(ri=0; ri<resNr; ri++)
resEmpty(res+ri);
360 fclose(fp); remove(colfile);
return(9);
362 while(resvoi1!=NULL) {
363 if(resvoi2==NULL || strcasecmp(resvoi1->
name, resvoi2->
name)) {
364 strcpy(tmp, resvoi1->
name);
365 if(verbose>10) printf(
"NEW name := %s\n", resvoi1->
name);
367 while((cptr=strpbrk(tmp,
" ."))!=NULL) *cptr=
'_';
368 fprintf(fp,
" <li><a href=\"#%s\">%s</a></li>\n", tmp, resvoi1->
name);
370 resvoi2=resvoi1;
if(verbose>11) printf(
"name := %s\n", resvoi2->
name);
373 fprintf(fp,
"</ul>\n");
374 fprintf(fp,
"</div>\n");
375 }
else if(doMode==2) {
377 fprintf(fp,
"\n<div id=\"parcontainer\">\n");
378 fprintf(fp,
"<ol id=\"parlist\">\n");
379 for(pi=0; pi<res[0].
parNr; pi++) {
380 sprintf(tmp,
"par%d", pi+1);
381 fprintf(fp,
" <li><a href=\"#%s\">%s</a></li>\n", tmp, res[0].parname[pi]);
383 fprintf(fp,
"</ol>\n");
384 fprintf(fp,
"</div>\n");
388 fprintf(fp,
"\n<div id=\"tables\">\n");
395 if(verbose>1) printf(
"collecting results of one region at a time\n");
398 fprintf(stderr,
"Error: out of memory.\n");
399 for(ri=0; ri<resNr; ri++)
resEmpty(res+ri);
405 fprintf(stderr,
"Error: cannot get result file contents.\n");
406 for(ri=0; ri<resNr; ri++)
resEmpty(res+ri);
407 fclose(fp); remove(colfile);
return(9);
409 coll.
voiNr=0; vi=0; ret=0;
410 while(resvoi1!=NULL && ret==0) {
411 if(resvoi2==NULL || strcasecmp(resvoi1->
name, resvoi2->
name)) {
419 if(verbose>0) printf(
"region_name[%d] := %s\n", vi, resvoi1->
name);
421 char *cptr=strstr(coll.
datafile,
" . .");
if(cptr!=NULL) *cptr=(char)0;
423 for(pi=0; pi<coll.
parNr; pi++) {
424 strcpy(coll.
parname[pi], res[ri].parname[pi]);
425 strcpy(coll.
parunit[pi], res[ri].parunit[pi]);
431 if(verbose>2) printf(
"studynr := %s\n", res[ri].studynr);
436 st=localtime(&res[ri].time);
437 if(st!=NULL) strftime(tmp, 256,
"%Y-%m-%d", st);
else strcpy(tmp,
" ");
441 for(pi=0; pi<res[ri].
parNr; pi++)
451 }
else if (doMode==1) {
455 if(verbose>1) printf(
"writing result files as HTML tables\n");
456 for(ri=0, ret=0; ri<resNr; ri++) {
459 for(vi=0; vi<res[ri].
voiNr; vi++)
for(pi=0; pi<res[ri].
parNr; pi++)
460 res[ri].voi[vi].cl1[pi]=res[ri].voi[vi].cl2[pi]=
461 res[ri].voi[vi].sd[pi]=nan(
"");
464 if(ri>0) fprintf(fp,
"\n<br />\n");
469 }
else if(doMode==2) {
473 if(verbose>1) printf(
"collecting results of one parameter at a time\n");
475 for(ri=0, ret=0; ri<resNr; ri++) {
476 for(vi=0; vi<res[ri].
voiNr; vi++)
for(pi=0; pi<res[ri].
parNr; pi++)
477 res[ri].voi[vi].cl1[pi]=res[ri].voi[vi].cl2[pi]=
478 res[ri].voi[vi].sd[pi]=nan(
"");
482 fprintf(stderr,
"Error: out of memory.\n");
483 for(ri=0; ri<resNr; ri++)
resEmpty(res+ri);
491 coll.
time=time(NULL);
492 for(pi=0; pi<resNr; pi++) {
493 strcpy(coll.
parname[pi], res[pi].studynr);
495 strcpy(coll.
parname[pi++],
"Median");
496 strcpy(coll.
parname[pi++],
"Mean");
497 strcpy(coll.
parname[pi++],
"SD");
498 for(ri=0; ri<res[0].
voiNr; ri++) {
499 strcpy(coll.
voi[ri].
name, res[0].voi[ri].name);
500 strcpy(coll.
voi[ri].
voiname, res[0].voi[ri].voiname);
502 strcpy(coll.
voi[ri].
place, res[0].voi[ri].place);
506 for(pi=0; pi<res[0].
parNr; pi++) {
507 strcpy(coll.
program, res[0].parname[pi]);
509 for(ri=0; ri<res[0].
voiNr; ri++) {
510 for(vi=0; vi<resNr; vi++) {
519 if(pi>0) fprintf(fp,
"\n<br />\n");
521 fprintf(fp,
"\n<a id=\"par%d\"></a>\n", pi+1);
528 fprintf(stderr,
"Error: cannot tabulate result files.\n");
530 fclose(fp); remove(colfile);
return(9);
534 if(verbose>0) fprintf(fp,
"\n");
536 if(verbose>1) printf(
"closing collection file.\n");
537 fprintf(fp,
"</div>\n");
538 fprintf(fp,
"</body></html>\n");
541 if(verbose>0) printf(
"%s written.\n", colfile);
545 for(ri=0; ri<resNr; ri++)
resEmpty(res+ri);
569 if(resIndex!=NULL) *resIndex=0;
570 if(reslist==NULL || resNr<1)
return(NULL);
571 if(reslist[0].voiNr==0)
return(NULL);
573 if(lastResVOI==NULL) {
574 for(ri=0; ri<resNr; ri++)
for(vi=0; vi<reslist[ri].
voiNr; vi++)
575 reslist[ri].voi[vi].sw2=0;
577 return(reslist[0].voi);
580 for(ri=0; ri<resNr; ri++)
581 for(vi=0; vi<reslist[ri].
voiNr; vi++)
if(reslist[ri].voi[vi].sw2==0)
582 if(strcasecmp(reslist[ri].voi[vi].name, lastResVOI->
name)==0) {
583 reslist[ri].
voi[vi].
sw2=1;
if(resIndex!=NULL) *resIndex=ri;
584 return(reslist[ri].voi+vi);
587 for(ri=0; ri<resNr; ri++)
588 for(vi=0; vi<reslist[ri].
voiNr; vi++)
if(reslist[ri].voi[vi].sw2==0) {
589 reslist[ri].
voi[vi].
sw2=1;
if(resIndex!=NULL) *resIndex=ri;
590 return(reslist[ri].voi+vi);
610 char *cptr, tmp[1024];
616 if(fp==NULL || coll==NULL || coll->
voiNr<0 || collNr<1)
return(1);
617 if(coll->
voiNr<1)
return(0);
621 data=(
double*)malloc(coll->
voiNr*
sizeof(
double));
622 for(pi=0; pi<coll->
parNr; pi++) {
629 resMean(data, coll->
voiNr, &resstat[3][pi], &resstat[4][pi]);
636 fprintf(fp,
"\n<br />\n");
642 while((cptr=strpbrk(tmp,
" ."))!=NULL) *cptr=
'_';
643 fprintf(fp,
"\n<a id=\"%s\"></a>\n", tmp);
646 fprintf(fp,
"<table>\n");
647 fprintf(fp,
" <thead>\n");
650 fprintf(fp,
" <tr><th colspan=\"%d\">", 3+coll->
parNr);
652 fprintf(fp,
"</th></tr>\n");
655 fprintf(fp,
" <tr>\n <td>Nr</td><td>Study</td><td>Date</td>\n");
657 for(pi=0; pi<coll->
parNr; pi++)
658 fprintf(fp,
" <td>%s</td>\n", coll->
parname[pi]);
659 fprintf(fp,
" </tr>\n");
660 fprintf(fp,
" </thead>\n");
663 for(pi=0; pi<coll->
parNr; pi++)
667 fprintf(fp,
" <tbody>\n");
668 for(si=0; si<coll->
voiNr; si++) {
669 if(si%2) strcpy(tmp,
"evenstudy");
else strcpy(tmp,
"oddstudy");
670 fprintf(fp,
" <tr class=\"%s\">\n", tmp);
671 fprintf(fp,
" <td>%-3d</td>\n", si+1);
672 fprintf(fp,
" <td>%s</td>\n", coll->
voi[si].
name);
674 for(pi=0; pi<coll->
parNr; pi++) {
676 switch(partype[pi]) {
677 case 0: sprintf(tmp,
"%.0f", coll->
voi[si].
parameter[pi]);
break;
678 case 1: sprintf(tmp,
"%.*f", n, coll->
voi[si].
parameter[pi]);
break;
680 default: sprintf(tmp,
"%.*e", n, coll->
voi[si].
parameter[pi]);
break;
682 fprintf(fp,
" <td>%s</td>\n", tmp);
684 fprintf(fp,
" </tr>\n");
686 fprintf(fp,
" </tbody>\n");
690 fprintf(fp,
"</table>\n");
695 fprintf(fp,
" <tbody>\n");
697 fprintf(fp,
" <tr class=\"oddsum\">\n");
698 fprintf(fp,
" <td colspan=\"3\">Min</td>\n");
699 for(pi=0; pi<coll->
parNr; pi++) {
700 if(resstat[0][pi]>=0) n=4;
else n=3;
701 switch(partype[pi]) {
702 case 0: sprintf(tmp,
"%.0f", resstat[0][pi]);
break;
703 case 1: sprintf(tmp,
"%.*f", n, resstat[0][pi]);
break;
705 default: sprintf(tmp,
"%.*e", n, resstat[0][pi]);
break;
707 fprintf(fp,
" <td>%s</td>\n", tmp);
709 fprintf(fp,
" </tr>\n");
711 fprintf(fp,
" <tr class=\"evensum\">\n");
712 fprintf(fp,
" <td colspan=\"3\">Max</td>\n");
713 for(pi=0; pi<coll->
parNr; pi++) {
714 if(resstat[1][pi]>=0) n=4;
else n=3;
715 switch(partype[pi]) {
716 case 0: sprintf(tmp,
"%.0f", resstat[1][pi]);
break;
717 case 1: sprintf(tmp,
"%.*f", n, resstat[1][pi]);
break;
719 default: sprintf(tmp,
"%.*e", n, resstat[1][pi]);
break;
721 fprintf(fp,
" <td>%s</td>\n", tmp);
723 fprintf(fp,
" </tr>\n");
725 fprintf(fp,
" <tr class=\"oddsum\">\n");
726 fprintf(fp,
" <td colspan=\"3\">Median</td>\n");
727 for(pi=0; pi<coll->
parNr; pi++) {
728 if(resstat[2][pi]>=0) n=6;
else n=5;
729 switch(partype[pi]) {
730 case 0: sprintf(tmp,
"%.2f", resstat[2][pi]);
break;
731 case 1: sprintf(tmp,
"%.*f", n, resstat[2][pi]);
break;
733 default: sprintf(tmp,
"%.*e", n, resstat[2][pi]);
break;
735 fprintf(fp,
" <td>%s</td>\n", tmp);
737 fprintf(fp,
" </tr>\n");
739 fprintf(fp,
" <tr class=\"evensum\">\n");
740 fprintf(fp,
" <td colspan=\"2\">Mean</td>\n");
741 fprintf(fp,
" <td>n=%d</td>\n", coll->
voiNr);
742 for(pi=0; pi<coll->
parNr; pi++) {
743 if(resstat[3][pi]>=0) n=6;
else n=5;
744 switch(partype[pi]) {
745 case 0: sprintf(tmp,
"%.2f", resstat[3][pi]);
break;
746 case 1: sprintf(tmp,
"%.*f", n, resstat[3][pi]);
break;
748 default: sprintf(tmp,
"%.*e", n, resstat[3][pi]);
break;
750 fprintf(fp,
" <td>%s</td>\n", tmp);
752 fprintf(fp,
" </tr>\n");
754 fprintf(fp,
" <tr class=\"oddsum\">\n");
755 fprintf(fp,
" <td colspan=\"3\">SD</td>\n");
756 for(pi=0; pi<coll->
parNr; pi++) {
757 if(resstat[4][pi]>=0) n=6;
else n=5;
758 switch(partype[pi]) {
759 case 0: sprintf(tmp,
"%.2f", resstat[4][pi]);
break;
760 case 1: sprintf(tmp,
"%.*f", n, resstat[4][pi]);
break;
762 default: sprintf(tmp,
"%.*e", n, resstat[4][pi]);
break;
764 fprintf(fp,
" <td>%s</td>\n", tmp);
766 fprintf(fp,
" </tr>\n");
767 fprintf(fp,
" </tbody>\n");
770 fprintf(fp,
"</table>\n");
int backupExistingFile(char *filename, char *backup_ext, char *status)
Header file for libtpccurveio.
int resSetmem(RES *res, int voiNr)
int resMatchRegions(RES *res1, RES *res2)
int resMean(double *data, int nr, double *mean, double *sd)
void resSortByName(RES *res)
int resWriteXHTML11_head(FILE *fp, char *author_name)
int resParameterPrintType(RES *res, int parIndex)
int resWriteXHTML11_doctype(FILE *fp)
int resMatchParameternames(RES *res1, RES *res2)
int resIsDuplicateNames(RES *res)
int resRead(char *filename, RES *res, int verbose)
int resWriteHTML_table(RES *res, FILE *fp)
int resMedian(double *data, int nr, double *median, double *min, double *max)
Header file for libtpcmisc.
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
void strCleanForXML(char *s)
size_t strlcpy(char *dst, const char *src, size_t dstsize)
void tpcProgramName(const char *program, int version, int copyright, char *prname, int n)
int tpcHtmlUsage(const char *program, char *text[], const char *path)
void tpcPrintBuild(const char *program, FILE *fp)
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
ResVOI * resGetNextResVOI(RES *reslist, int resNr, ResVOI *lastResVOI, int *resIndex)
int rescoll_tabulate(FILE *fp, RES *coll, int collNr)
char parname[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
char datafile[FILENAME_MAX]
char parunit[MAX_RESPARAMS][MAX_RESPARNAME_LEN+1]
double parameter[MAX_RESPARAMS]
char hemisphere[MAX_REGIONSUBNAME_LEN+1]
char place[MAX_REGIONSUBNAME_LEN+1]
char name[MAX_REGIONNAME_LEN+1]
char voiname[MAX_REGIONSUBNAME_LEN+1]