Académique Documents
Professionnel Documents
Culture Documents
Applications Development
INTRODUCTION
Sending out bulk emails to multiple recipients with corresponding attachments from email software such as Microsoft Outlook is not an easy task. Bulk emails can be error-prone as it is not unheard of to inadvertently attach and send an incorrect file to the wrong recipient. With SASs data step and EMAIL facilities, the email task can be automated with the simple implementation of SAS code. This paper presents an easy data driven SAS email application using SAS EMAIL capabilities.
NESUG 2010
Applications Development
Example 1: The following example shows how to issue a simple message with an attachment file using the FILENAME options: filename outmail email "xiqun@yahoo.com" subject="Lab Outlier Report.doc" cc="xiqun_huang@merck.com" attach="c:\reports\Lab Outlier Report.doc"; data _null_; file outmail; put 'Dear Investigator,'; put 'Attached is your site Lab Outlier Report for study 0938-45'; put 'Please check and let me know if you have any question'; put; put 'Regards,'; put; put 'Jade Huang'; run; Screen shot from Outlook:
Example 2: Use Email options specified in FILE statement to override the options in a FILENAME statement. filename outmail email "xiqun@yahoo.com" ; data _null_; file outmail to="wenjie.wang@klconsultingservices.com" cc="xiqun_huang@merck.com" subject="Invoice Report.doc" attach="c:\reports\Invoice report.doc" ; put 'Dear Wenjie'; put 'Attached is your last month invoice report.'; put 'If you have any question, please feel free to call.'; put; put 'Regards,'; put 'Jade Huang'; run; The following screen shot shows that the option in the FILENAME statement is overwritten.
NESUG 2010
Applications Development
Example 3: The directives in the PUT statement will override the corresponding option defined at the FILENAME or FILE statement as shown below. filename outmail email ; data _null_; file outmail; PUT '!EM_TO! wenjie.wang@klconsultingservices.com'; PUT '!EM_SUBJECT! Reminder'; PUT '!EM_CC! xiqun_huang@merck.com'; PUT '!EM_ATTACH! c:\reports\Insurance Claim Report.doc'; put 'Dear Accountant,'; put 'This is a reminder, please process the attached invoice within'; put '7 days to claim money from the insurance provider.'; put; put 'Regards,'; put; put 'Jade Huang'; run;
NESUG 2010
Applications Development
The following is an example of a requirement for a SAS Email Application: In a hospital, there are SOPs regarding Residents Notes Writing which regulate that each attending resident physician write a note within 24 hours of each admission; the notes could be written by his/her assistant or nurse. If he/she did not write notes within 24 hours of an admission, at the end of that week, he/she will get an email with an attachment which shows the admission case lacking notes. This process can be automated with the emanagement tool to increase the SOP compliance rate; macros are used to accomplish this task. The macro will have two parameters called FROM and TO which denote the report period from which to extract data; the detail coding is in the appendix. Input dataset- NOTE: Note_EIN could be either a doctor or nurse employee identifier
Input dataset- ADMISSION: Here ADM_EIN is the attending Physician identification number
Emp (Employer information): Employee EIN, first, last name, email address, etc
NESUG 2010
Applications Development
Merge the admission and notes files and select the admission cases which lack notes during a certain week range(Noncompli dataset) based on the two macro parameters: from date and to date in the format of DATE9. Find out the physicians who are not in compliance: Noncompli_ein ein 1002 firstname Wenjie lastname Wang email_address wenjie.wang@klconsultingservices.com
2. %macro update_noncompli_email_list: generate an rtf file associated with each attending physician's non-compliant admission cases and populate the noncompli_email_list table for sending out email later on. This part is the key part of the application. o Compile %macro update_noncompli_email_list( ) o Prepare noncompli_email_list table structure: Ein o emp_name email_address attach_file
Execute the macro %update_noncompli_email_list to populate the dataset non_compli_email_list based on each iteration of the input dataset noncompli_ein:
ein
firstname
lastname
email_address
/* use execute routine to pass each observation to a macro from data step */ call execute( '%update_noncompli_email_list(ein='||ein|| ',Physician_name='||compress(firstname)|| '_'||compress(lastname)|| ', name='||compress(firstname)|| ' '||compress(lastname)|| ', email_address='||compress(email_address)|| ') ); RUN; %MACRO update_noncompli_email_list( ein= , physician_name= , name= , email_address=); filename attfile "c:\ Admission_Audit_Report_for_&physician_name._ Wkof&to..rtf"; ods listing close; ods rtf file= attfile bodytitle; %* create attachment for each selected non compliant physician, the output screen shot is as follows; Admission Audit Report for Wenjie Wang from 20JUL2009 to 27JUL2009 The following is a list of admissions which has no notes as of 28JUL2009 Room and Bed
Ward
Reason No Notes
20JUL2009:08:01:00 789098099 James Patterson Internal B00301-A 20JUL2009:09:01:00 235345691 Lisa Lowers 21JUL2009:11:20:00 987098000 Brian Jacquers ods rtf close; ods listing; %* insert into noncompli_email_list ; Ein emp_name email_address 1002 Wenjie wenjie.wang@klcons Wang ultingservices.com %MEND update_noncompli_email_list; 3. Email with an attachment using noncompli_email_list dataset as input: filename myemail email; data _null_;
NESUG 2010
Applications Development
set noncompli_email_list; %* input dataset; .. Run; Screen shot of the email sent by the application:
4. To further automate the procedure, put it in Windows Scheduler or Unix Cron Job.
CONCLUSION
SAS Email Access Method combined with the powerful language of SAS can turn a lengthy repetitive administrative task into a quick and error-proof job. The macro demonstrated above is very flexible and easy to modify and can be implemented to fit your business needs.
REFERENCE
Using Groupware to Distribute SAS Data at April 2009 http://v8doc.sas.com/sashtml/ Wenjie Wang and Simon Lin Using SAS to Send Bulk Emails with Attachments Proceedings of the Pharmaceutical Industry SAS Users, May 2007 ACKNOWLEDGMENTS SAS and all other SAS Institute Inc. product or service names are registered trademarks or trademarks of SAS Institute Inc. in the USA and other countries. indicates USA registration. Other brand and product names, such as Microsoft Outlook are registered trademarks or trademarks of their respective companies.
CONTACT INFORMATION
Wenjie Wang Programmer Consultant Sun Solutions Inc, Clinton, NJ (908) 660-4933 Wwang9235@gmail.com Jade Huang Merck & Co., Inc. (732) 594-3854 xiqun_Huang@merck.com Simon Lin Eisai Inc. (201) 627-2866 Simon_lin@eisai.com
APPENDIX
libname indata "u:\2009\paper"; %macro notes_report(from=, to=); %let today=%sysfunc(today(),date9.); /* capture report running date*/ %********************************************************************; %* Step 1: get the admission file and notes file *; %********************************************************************;
NESUG 2010
Applications Development
proc sort data=indata.admission(where=(input("&from", date9.)<=datepart(admission_dt)<=input("&to", date9.))) out=admission; by adm_caseid; run; proc sort data=indata.notes(where=(input("&from", date9.)<=datepart(notes_dt)<=input("&to", date9.)+1)) out=notes; by adm_caseid; run; %********************************************************************; %* Step 2: merge the two files to get the admission case which lack *; %* notes or has delayed notes *; %********************************************************************; %* for each admission, there should have a note associated with it *; data allnotes; merge admission(in=a) notes(in=b drop=patientid patientname); by adm_caseid; tminterval=intck('hour',admission_dt, notes_dt); if intck('hour',admission_dt, notes_dt)>24 then do; notes=1; compliance=0; delay=1; end; else if intck('hour',admission_dt, notes_dt)= . then do; notes=0; compliance=0; delay=.; end; else if intck('hour',admission_dt, notes_dt) <=24 then do; notes=1; compliance=1; delay=0; end; else compliance=.; run; data noncompli; set allnotes; where delay ne 0; run; proc format; value delayf 1="Delayed Notes" .="No Notes"; run; %********************************************************************; %* Step 3: create a macro to generate a rtf file for each attending *; %* physician who did not write a notes or delayed in notes *; %* writing, and in the mean time, populate the *; %* noncompli_email_list table for sending out email later on*; %********************************************************************; %macro update_noncompli_email_list( ein=, physician_name=, name=, email_address=); %* get the attending physician name, email address and save them *;
NESUG 2010
Applications Development
%* to macro variable *; filename attfile"u:\2009\paper\Admission_Audit_Report _for_&physician_name._Wkof&to..rtf"; ods listing close; ods rtf file= attfile bodytitle; title1 "Admission Audit Report for &name from &from to &to."; title2 "The following is a list of admission which has no notes or delayed notes as of &today"; proc sql; select admission_dt label='Admission Date/Time', patientid label='Patient ID', patientname label='Patient Name', ward label='Ward', room_bed label='Room and Bed', delay label='Reason' format=delayf. from noncompli where ADM_EIN=&ein; quit; ods rtf close; ods listing; %*** Now populate the nonotes_email_list table ***; proc sql; insert into noncompli_email_list values (&ein, "&name", "&email_address", "u:\2009\paper\Admission_Audit_Report_for_&physician_name._Wkof&to. .rtf"); quit; %mend update_noncompli_email_list; %********************************************************************; %* Step 4: find out how many physician is not in compliance *; %********************************************************************; proc sort data=noncompli out=noncompli_adm_ein(keep=adm_ein) nodupkey; by adm_ein; run; proc sql; create table noncompli_ein as select a.* from indata.emp as a, noncompli_adm_ein as b where a.ein=b.adm_ein ; quit; %********************************************************************; %* Step 5: execute macro to generate the report for each *; %* non-compliant physician and update the *; %* non_compli_email_list dataset with the name and location *; %* for each report file, email_address,physician name, etc. *; *; %********************************************************************; proc sql; drop table noncompli_email_list; create table noncompli_email_list (ein num, emp_name char(60), email_address char(60), attach_file char(100)); quit; Data _null; set noncompli_ein; /* executing macro: update_noncompli_email_list from data step */
NESUG 2010
Applications Development
call execute('%update_noncompli_email_list(ein='||ein|| ',Physician_name='||compress(firstname)|| '_'||compress(lastname)|| ', name='||compress(firstname)|| ' '||compress(lastname)|| ', email_address='||compress(email_address)|| ')' ); run; %********************************************************************; %* Step 6: Send email to each non-compliant physician with the *; %* corresponding attachment *; %********************************************************************; filename myemail email; data _null_; set noncompli_email_list; file myemail; put '!EM_TO!' email_address; put '!EM_SUBJECT! Admission Audit Report for ' emp_name; put "Dear Provider " emp_name ":" ; put "According to the medical record, you are either the "; put "inpatient attending physician or the attending in "; put "charge of the ward for the patient(s) listed below. "; put "An attending admission note is required by policy "; put "within one calendar day from the time of admission, "; put "but could not be found for the patient(s) listed. "; put "Please place your attending note in system ASAP. "; put "Thank you for your attention. "; put '!EM_ATTACH!' attach_file; put '!EM_SEND!'; put '!EM_NEWMSG!'; put '!EM_ABORT!'; run; %mend notes_report; %notes_report(from=20JUL2009, to=27JUL2009);