%let name=adverse_event;
filename odsout '.';

/*
SAS/Graph version of Sanjay's plot:
http://blogs.sas.com/content/graphicallyspeaking/2012/02/09/sg-procedures-book-samples-adverse-event-timeline/
*/

%let pat_id=xx-xxx-xxxx;
%let increment=7;

/*
Date for the start of the study, and the number of weeks before & after
(ie, to the left & right) to show on the graph...
*/
data timeline;
informat start_date date9.;
format start_date date9.;
input start_date weeks_before weeks_after;
datalines;
15mar2013 2 14
;
run;

/* create some macro variables for the axes */
proc sql noprint;
select start_date format=date9. into :start_date from timeline;
select start_date-(weeks_before*&increment) format=date9. into :min_date from timeline;
select start_date+(weeks_after*&increment) format=date9. into :max_date from timeline;
select 0-(weeks_before*&increment) into :min_days from timeline;
select 0+(weeks_after*&increment) into :max_days from timeline;
quit; run;


/*--Adverse Events timeline data--*/
data ae_data;
informat aestdate aeendate yymmdd10.;
format aestdate aeendate yymmdd10.;
input aeseq aedecod $ 5-39 aesev $ aestdate aeendate;
datalines;
1   DIZZINESS                           MODERATE  2013-03-06  2013-03-07          
2   COUGH                               MILD      2013-03-20  .                   
3   APPLICATION SITE DERMATITIS         MILD      2013-03-26  2013-06-18          
4   DIZZINESS                           MILD      2013-03-27  2013-03-27          
5   ELECTROCARDIOGRAM T WAVE INVERSION  MILD      2013-03-30  .                   
6   DIZZINESS                           MILD      2013-04-01  2013-04-11          
7   DIZZINESS                           MILD      2013-04-01  2013-04-11          
8   APPLICATION SITE DERMATITIS         MODERATE  2013-03-26  2013-06-18          
9   HEADACHE                            MODERATE  2013-05-17  2013-05-18          
10  APPLICATION SITE DERMATITIS         MODERATE  2013-03-26  2013-06-18          
11  PRURITUS                            SEVERE    2013-05-27  2013-06-18          
;
run;


/* Annotate the 'bar' segments for each event */
data anno_bar; set ae_data;
length function color $8 style $20;
xsys='2'; ysys='2'; when='a'; hsys='3';

function='move';
x=aestdate;
y=aeseq; 
output;

function='move';
y=y-.2; 
output;

function='bar'; line=0;
y=y+2*.2; 
x=aeendate; 
if (x=.) or (x>"&max_date"d) then x="&max_date"d;
if aestdate=aeendate then x=x+.5;  /* make each bar at least 1/2 day duration */
output;

run;

data anno_bar_solid; set anno_bar;
style='solid'; color='gray';
if aesev='MILD' then color="cx5fcf5f";
if aesev='MODERATE' then color="cxdfcf3f";
if aesev='SEVERE' then color="cxbf3f3f";
length html $300;
html=
 'title='||quote(
  trim(left(propcase(aedecod)))||'0d'x||
  trim(left(put(aestdate,yymmdd10.)))||' to '||trim(left(put(aeendate,yymmdd10.)))||'0d'x||
  trim(left(propcase(aesev))))||
 ' href="adverse_event_info.htm"';
run;

data anno_bar_outline; set anno_bar;
style='empty'; color="gray77";
run;


/* annotate the arrow for the bar segments that go off the chart */
data anno_arrow; set ae_data;
length function color $8 style $20;
xsys='2'; ysys='2'; when='a'; hsys='3';

if (aeendate>="&max_date"d or aeendate=.) then do;
 function='move';
 x="&max_date"d;
 y=aeseq; 
 output;

 function='draw'; size=.1; color="gray77";
 xsys='7';
 y=y-.30; x=0; output;
 y=y+.30; x=+2; output;
 y=y+.30; x=-2; output;
 y=y-.30; x=0; output;

 end;

run;



/* the 3 blank/legend values are to guarantee that all 3 colors show up in the legend,
   whether they're in this particular graph or not.
*/
data ae_legend;
informat aestdate aeendate yymmdd10.;
format aestdate aeendate yymmdd10.;
input aeseq aedecod $ 5-39 aesev $ aestdate aeendate;
datalines;
.                                       MILD      .           .           Legend
.                                       MODERATE  .           .           Legend
.                                       SEVERE    .           .           Legend
;
run;

data ae_data; set ae_data ae_legend;
aedecod=propcase(aedecod);
aesev=propcase(aesev);
run;

/*
User-defined format so that aeseq number prints as aedecod text.
(this way, the axis will be in the numeric order, but will show the text values)
*/
proc sql noprint;
create table foo as 
select unique aeseq as start, aedecod as label
from ae_data where aedecod^=''; 
quit; run;
data control; set foo; 
fmtname='aefmt';
type='N';
run;
proc format lib=work cntlin=control;
run;


/* annotate the slightly darker 'zero' line */
data anno_line;
length function color $8 style $20;
xsys='2'; ysys='1'; when='a';
x="&start_date"d;
y=0; function='move'; output;
y=100; function='draw'; color='grayaa'; output;
run;

data anno_top_axis;
length function color $8 style $20 text $10;
xsys='2'; ysys='1'; when='a';
y=100;
function='label'; position='2'; color=''; style='albany amt';
do days=&min_days to &max_days by &increment;
 text=trim(left(days));
 x="&start_date"d+days;
 output;
 end;
run;

data anno_all; set 
 anno_line 
 anno_bar_solid 
 anno_bar_outline 
 anno_arrow 
 anno_top_axis
 ;
run;


goptions device=png;
goptions ypixels=600 xpixels=900;
 
ODS LISTING CLOSE;
ODS HTML path=odsout body="&name..htm" 
 (title="Adverse Event Timeline Plot") 
 style=htmlblue;

goptions gunit=pct htitle=18pt htext=9pt;

axis1 order=("&min_date"d to "&max_date"d by &increment) minor=none      
 value=(angle=90) offset=(0,0) label=none;

axis2 label=none minor=none offset=(3,3);

legend1 label=none repeat=1 shape=symbol(.25in,.25in) offset=(15,0);

symbol1 value=squarefilled color=cx5fcf5f h=.1 interpol=none;
symbol2 value=squarefilled color=cxdfcf3f h=.1 interpol=none;
symbol3 value=squarefilled color=cxbf3f3f h=.1 interpol=none;

title1 ls=1.5 "Adverse Event Timeline for Patient ID = &pat_id";
title2 h=5pct " ";  /* some blank/white-space, for the annotated top axis numbers */

proc gplot data=ae_data anno=anno_all;
format aeseq aefmt.;
plot aeseq*aestdate=aesev / vreverse
 haxis=axis1 vaxis=axis2
 autohref chref=graydd lhref=1
 autovref cvref=gray99 lvref=33
 legend=legend1
 des='' name="&name";
run;

title;
proc print data=ae_data (where=(aestdate^=.)) noobs; 
var aeseq aestdate aeendate aedecod aesev;
run;

quit;
ODS HTML CLOSE;
ODS LISTING;
