Thursday, May 29, 2014

Evaluate True, for large report processing

Report Requirement

The processing required for many reports or file exports (csv) I've been creating have many data collection calls.  Sometimes multiple tables can be joined to reduce the number of unique calls but often the there are times where data is not found or the returned data needs some analysis.  If the design also calls for different reporting or actions based on the analysis it forces you to make many individual calls.  

In my example we are using one large SQL to query to get a initial set of Employee ID's.  With each employee ID I as asked to do about 10 other steps of data collection and analysis each step could lead to one of the following results:
  • Skip to the next Employee ID
  • Write the report row and skip to next Employee ID
  • Continue Processing
  • Insert Data 
My initial build was using a while loop through the employees with if conditions inside however it became apparent that my nested IF conditions were getting out of hand.

My Solution 

The solution I found to create very clean and readable code that also matched the design documentation was to use an EVALUATE TRUE statement.

As you can see the &report record is a derived record that is used to accumulate the data as we go.  I used local functions to do the mini calls if I the requirement is to skip an employee when a record is not found the function simply returns FALSE.  This will stop the processing of any more WHEN statements in the EVALUATE.  In some cases I return a whole record if the data is needed for future calls and in this case I do my check for a null record returned within the WHEN statement.

Main Loop Processing

  1. /* Data & Records */
  2. Local Record &admApplProg, &extAcadDataA, &extAcadDataB, &mcmFaErlyAvg;
  3. Local Record &report;
  4. /* Processing Variables */
  5. Local boolean &resumeEmplid;
  6.  
  7. &admApplProg = CreateRecord(Record.ADM_APPL_PROG);
  8. &Sql = CreateSQL(SQL.SQL1, &admApplProg);
  9. While &Sql.Fetch(&admApplProg)
  10. &resumeEmplid = True;
  11. &report = CreateRecord(Record.MCM_AD_FFA_RPT);
  12. &report.EMPLID.Value = &admApplProg.EMPLID.Value;
  13. &report.ADM_APPL_NBR.Value = &admApplProg.ADM_APPL_NBR.Value;
  14. &report.ACAD_CAREER.Value = &admApplProg.ACAD_CAREER.Value;
  15. &report.ACAD_PROG.Value = &admApplProg.ACAD_PROG.Value;
  16. &report.PROG_ACTION.Value = &admApplProg.PROG_ACTION.Value;
  17. Evaluate True
  18. When &resumeEmplid
  19. /* Drop if EXT_ORG_ID not found */
  20. &extAcadDataA = getMaxExtAcadData(&report, &admApplProg.EMPLID.Value);
  21. If None(&extAcadDataA) Then
  22. &resumeEmplid = False;
  23. End-If;
  24. When &resumeEmplid
  25. /* Valid Canadian Citizen */
  26. &resumeEmplid = isValidCndApplicant(&REPORT, &extAcadDataA);
  27. When &resumeEmplid
  28. /* retrieve an Education Source */
  29. &resumeEmplid = hasEducSrc(&report);
  30. When &resumeEmplid
  31. /* Read Adm_APPL_PLAN */
  32. &resumeEmplid = hasAcadPlan(&report, &admApplProg);
  33. When &resumeEmplid
  34. /* Retrieve the students averages */
  35. &resumeEmplid = hasMcmAvg(&report, &admApplProg);
  36. When &resumeEmplid
  37. /* read Early Financial Aid value*/
  38. &mcmFaErlyAvg = getErlyAvg(&report, &admApplProg);
  39. If None(&mcmFaErlyAvg) Then
  40. &resumeEmplid = False;
  41. End-If;
  42. When &resumeEmplid
  43. /* 9. Define the FA Year*/
  44. &resumeEmplid = hasFaYear(&report, &admApplProg);
  45. When &resumeEmplid
  46. /* Write the report row */
  47. writeRecordToFile(&report);
  48. End-Evaluate;
  49. End-While;

Functions

Here you'll see some functions that are returning records and some that return the Boolean of successful data retrieval or not.  I have not include all the functions just some of the examples.  One thing you will notice is that I'm passing the main &report record into the functions.  This is because PeopleCode uses pass by reference so you can set values in the local fuction &REPORT will be retained after your function returns to the mainline.

If you are curious about my exists check on function hasFaOffer I have previously blogged this example.

  1. /* Identify last school attended */
  2. Function getMaxExtAcadData(&REPORT) Returns Record;
  3. Local SQL &SqlExtAcad;
  4. Local Record &extAcadData;
  5. Local String &saveOrgId;
  6. &extAcadData = CreateRecord(Record.EXT_ACAD_DATA);
  7. &SqlExtAcad = CreateSQL(SQL.MAX_EXT, &extAcadData, &RECORD.EMPLID.value);
  8. While &SqlExtAcad.Fetch(&extAcadData)
  9. If None(&saveOrgId) Then
  10. &saveOrgId = &extAcadData.EXT_ORG_ID.Value;
  11. Else
  12. /* if more then one EXT_ORG_ID is found report and skip process */
  13. If &saveOrgId <> &extAcadData.EXT_ORG_ID.Value Then
  14. &REPORT.REASON.value = "Verify EXT_ORG_ID";
  15. Return Null;
  16. End-If;
  17. End-If;
  18. End-While;
  19. If None(&extAcadData.EXT_ORG_ID.Value) Then
  20. Return Null;
  21. End-If;
  22. Return &extAcadData;
  23. End-Function;
  24.  
  25. Function getErlyAvg(&REPORT, &AAP) Returns Record;
  26. Local string &qry, &result;
  27. Local Record &record = CreateRecord(Record.MCM_FA_ERLY_AVG);
  28. &qry = "%SelectAll(:1 A) A WHERE ...";
  29. SQLExec(&qry, &record, ... , &record);
  30. If All(&record.INSTITUTION.Value) Then
  31. &REPORT.MCM_FA_AWARD_VALUE.value = &record.MCM_FA_AWARD_VALUE.Value;
  32. &REPORT.MCM_FA_MIN_SCH_AVG.value = &record.MCM_FA_MIN_SCH_AVG.Value;
  33. &REPORT.MCM_FA_MAX_SCH_AVG.value = &record.MCM_FA_MAX_SCH_AVG.Value;
  34. Return &record;
  35. Else
  36. Return Null;
  37. End-If;
  38. End-Function;
  39.  
  40. Function hasFaYear(&REPORT, &AAP) Returns boolean;
  41. Local string &result;
  42. SQLExec(SQL.MCM_0784_SQL3, &AAP.EMPLID.value, ... , &result);
  43. &REPORT.AID_YEAR.value = &result;
  44. Return True;
  45. End-Function;
  46.  
  47. Function hasFaOffer(&REPORT, &AAP) Returns boolean;
  48. Local string &exists;
  49. Local number &result;
  50. SQLExec(SQL.MCM_0784_SQL4, &AAP.EMPLID.value, ... , &exists, &result);
  51. If All(&exists) Then
  52. &REPORT.EARLY_OFFER_TOTAL.value = &result;
  53. Return True;
  54. Else
  55. Return False;
  56. End-If;
  57. End-Function;
  58.  

Proof of Concept

Here is a small little code snippet I used as a proof of concept

  1. Function getTrue(&array) Returns boolean;
  2. MessageBox(0, "", 0, 0, " getTrue Function ");
  3. &array.Push("True");
  4. Return True;
  5. End-Function;
  6.  
  7. Function getFalse(&array) Returns boolean;
  8. MessageBox(0, "", 0, 0, " getFalse Function ");
  9. &array.Push("False");
  10. Return False;
  11. End-Function;
  12.  
  13. Local boolean &resume = True;
  14. Local array of string &evalCount;
  15.  
  16. &evalCount = CreateArrayRept("", 0);
  17.  
  18. Evaluate True
  19. When &resume
  20. MessageBox(0, "", 0, 0, " Evaluate 1 ");
  21. &evalCount.Push("1");
  22. When &resume
  23. MessageBox(0, "", 0, 0, " Evaluate 2 ");
  24. &evalCount.Push("2");
  25. &resume = getTrue(&evalCount);
  26. When &resume
  27. MessageBox(0, "", 0, 0, " Evaluate 3 ");
  28. &evalCount.Push("3");
  29. When &resume
  30. MessageBox(0, "", 0, 0, " Evaluate 4 ");
  31. &evalCount.Push("4");
  32. &resume = getFalse(&evalCount);
  33. When &resume
  34. MessageBox(0, "", 0, 0, " Evaluate 5 ");
  35. &evalCount.Push("5");
  36. When &resume
  37. MessageBox(0, "", 0, 0, " Evaluate 6 ");
  38. &evalCount.Push("6");
  39. &resume = getTrue(&evalCount);
  40. End-Evaluate;
  41.  
  42. MessageBox(0, "", 0, 0, " Array %1 ", &evalCount.Join(", "));

No comments:

Post a Comment