Source Code for Report Framework

Documentation

Click here for actual source code and sample data in Zip format

It is written in Microsoft ASP and uses the Access 97 Jet database engine. This choice of development instruments is not meant as an endorsement of Microsoft products. It could have just as well been done in PHP and MySQL, for example.

Note that the packaging process may make the capitalization odd for some of the included files. You may have to rename them. (Case sensitivity is annoying. Fix it Linus!)

Below is a copy of the main module. Some character combinations may not display properly in some browsers. Use the Zip version (above) if source code accuracy is desired. The listing below is only for quick reference.



<%option explicit
  REM - Reporting Framework Demo    (build no. 2)
  REM - HTTP Parameters: rpt_prompt (yes,no), rpt_show (yes,no)
  REM - From: http://oocities.com/tablizer/chal05.htm
  REM - This software cannot be used for commercial purposes
  REM - without written permission from the author.
  REM - Copyright 2001 by Findy Services and B. Jacobs.
%>

<!--#include file="utils.asp"-->

<body bgcolor="white">

<%

' Declare module-level vars
dim promptCrit, showReport, saveCrit, userID, errMsgs, reportID

' Constants
dim DBconst_true   ' what the DB uses for True values (varies per vendor)
DBconst_true = -1  ' for MS-Jet
dim DBconst_dateDelim    ' date delimiter
DBconst_dateDelim = "#"  ' MS-Jet

' Initalize
promptCrit = (lcase(request("rpt_prompt")<>"no"))
saveCrit =   (lcase(request("rpt_saveValues")="yes"))
showReport = (lcase(request("rpt_show")="yes"))              
userID =     session("userID")
errMsgs =    space(0)
reportID =   request("reportID")

if isBlank(reportID) then
  hout "<p><b>** REPORT NOT FOUND ** Try the <a href=""rptlist.asp"">list</a><p>"
  response.end
end if

if saveCrit then
  saveCritValues()
end if

if showReport then
  validateCriteria
  if Not hasErrors() then
    genReport
  end if
end if

if promptCrit then
  criteriaPrompts
end if

' Bottom navigation

%>
<div align="center">
  <hr>
  <a href="rptlist.asp">Reports List</a>
  <hr>
</div>
<%

'----------------------------
sub criteriaPrompts()  ' display report criteria prompts
  dim rs, sql, theVal, useWidth, useValue, temp, fldRef

  if hasErrors() then     ' Display any validation error messages
     hout "<font color=red><b>** PLEASE NOTE THE FOLLOWING ERRORS **</b></font>"
     hout "<ul>" & errMsgs & "</ul><p>"
  end if
  initializeUserReport userID, reportID
  ' Use report and report field description tables to build prompts
  sql = reportFieldsSQL(userID, reportID)
  set rs = stdConn.execute(sql)
  if rs.eof then
    hout " SORRY, Report items not set up yet "
  else
    hout "<h3>" & rs("reportTitle") & " Report Criteria </h3>"
    hout "<form method=post action=""report.asp"">"
    do while not rs.eof           ' for each prompt field
      useWidth = clng("0" & trim(rs("width")))
      if useWidth = 0 then
        useWidth = 20             ' default width if blank or zero
      end if
      fldRef = "fld_" & rs("itemID")
      useValue = trim(rs("fldValue") & "")
      hout titlePadder(rs("fldTitle"), Not rs("keepwithPrior"), 20)
      select case ucase(rs("fmtType"))
      case "T","N","D"    ' text, number, or date
        hout inputBox("text", fldRef, useValue, useWidth, useWidth)
      case "Y"    ' Boolean
        temp = trim(lcase(rs("fldValue")) & "")
        if isBlank(temp) or temp="(either)" then
          useValue = "(either)"
        elseif contains("1,true,yes,on", temp) then
          useValue = "Yes"
        else
          useValue = "No"
        end if
        hout pickList1(fldRef, useValue, "(either),Yes,No")
      case "L"    ' List
        hout pickList1(fldRef, useValue, "(any)," & rs("theList"))
      end select

      rs.movenext()
    loop
    hout "<p>"
    hout inputBox("hidden","rpt_saveValues","yes",0,0)
    hout inputBox("hidden","rpt_show","yes",0,0)
    hout inputBox("hidden","rpt_prompt",request("rpt_prompt"),0,0)
    hout inputBox("hidden","reportID",reportID,0,0)
    hout inputBox("submit","btnSubmit","View Report",0,0)
    hout " &\nbsp; <a href=""under.asp"">Clear Criteria</a>"
    hout "</form>"
  end if
  rs.close
end sub
'-------------------------------------
sub ValidateCriteria()
  dim sql, rs, fldValue, fmtType
  sql = reportFieldsSQL(userID, reportID)
  set rs = stdConn.execute(sql)
  do while not rs.eof    ' for each report crit field
    fldValue = trim(rs("fldValue") & "")
    fmtType = ucase(trim(rs("fmtType") & ""))
    if fmtType="N" and len(fldValue) > 0 then         
      if not isNumeric(fldValue) then
        appendErr "Invalid Number: " & fldValue
      end if
    end if
    if fmtType="D" and len(fldValue) > 0 then         
      if not isDate(fldValue) then
        appendErr "Invalid Date: " & fldValue
      end if
    end if
    if rs("Required") and len(fldValue)=0 then
      appendErr "Field is Required: '" & rs("fldTitle") & "'"
    end if
    rs.moveNext
  loop
  rs.close
end sub
'-------------------------------------
sub saveCritValues()  ' save criteria responses to table from HTTP
  dim sql, sql2, rs
  sql = reportFieldsSQL(userID, reportID)
  stdConn.beginTrans   ' MS-Jet bug workaround
  set rs = stdConn.execute(sql)
  do while not rs.eof    ' for each report crit field
    sql2 = "UPDATE userFields SET fldValue='" & trim(request("fld_" & rs("itemID"))) & "' "
    sql2 = sql2 & " WHERE userID=" & userID & " AND rptItemID=" & rs("itemID")
    stdConn.execute(sql2)
    rs.moveNext
  loop
  rs.close
  stdConn.commitTrans
  delay(1)  ' MS-Jet bug workaround
end sub
'-------------------------------------
sub genReport()       ' Generate report based on built-up SQL statement
  dim sql, rs, fldValue, useValue, fmtType, useComparer, orderByClause
  dim whereClause, groupByClause, temp, title

  set rs = stdConn.execute(reportFieldsSQL(userID, reportID))
  if rs.eof then
    hout " ** REPORT ERROR. Contact admin. ** "
  else
    sql = "SELECT " & rs("selectClause") & " FROM " & rs("fromClause")
    title = rs("reportTitle")
    whereClause =   rs("whereClause")
    groupByClause = rs("groupByClause")
    orderByClause = rs("orderByClause")
    do while not rs.eof    ' for each report crit field
      fldValue = trim(rs("fldValue") & "")
      fmtType = ucase(trim(rs("fmtType") & ""))
      useValue = fldValue

      select case fmtType
      case "T"
         if isBlank(rs("comparer")) then
           useValue = "'%" & useValue & "%'"        ' for LIKE
         else
           useValue = "'" & useValue & "'"        ' quote wrap
         end if
      case "D"
         useValue = DBconst_dateDelim & useValue & DBconst_dateDelim
      case "N"
         ' leave as is
      case "Y"
         select case lcase(fldValue)
         case "(either)",""
           useValue = space(0)
         case "yes"
           useValue = DBconst_true
         case "no"
           useValue = 0
         end select
      case "L"
         if lcase(useValue) = "(any)" then
           useValue = space(0)
         else
           useValue = "'" & useValue & "'"
         end if
      end select

      '---Comparer
      if isblank(rs("comparer")) then
        useComparer = " = "
        if fmtType = "T" then
          useComparer = " LIKE "
        end if
      else
        useComparer = space(1) & rs("comparer") & space(1)
      end if

      '---Append field as an AND clause
      if not isBlank(useValue) and not isBlank(fldValue) then
        temp = rs("fldName") & useComparer & useValue
        if not isBlank(whereClause) then
          temp = " AND " & temp
        end if
        concat whereClause, temp
      end if
      '---
      rs.moveNext
    loop
    if not isBlank(whereClause) then
      whereClause = " WHERE " & whereClause     ' filler start because of AND statements
    end if
    if not isBlank(groupByClause) then
      groupByClause = " GROUP BY " & groupByClause
    end if
    sql = sql & whereClause & groupByClause & " ORDER BY " & orderByClause
    hout "<BR><B>TEST:</B> " & sql & "<BR>"
    displayQuery  sql, title ,100
  end if
  rs.close
end sub
'-------------------------------------
sub displayQuery(sql, title, maxRows)  ' display SQL query as an HTML table
  dim lineCnt, i, temps, maxCell, rs

  maxCell = 100   ' max field size
  set rs = stdConn.execute(sql)
  if rs.eof then
    hout "<p>SORRY, no matching records were found. Please try again.<p>"
  else
    lineCnt = 0
    hout "<h3>" & title & "</h3>"
    hout "<TABLE border=1 cellpadding=2 cellspacing=0>"
    do While Not rs.EOF and lineCnt < maxRows
      lineCnt = lineCnt + 1
      if (lineCnt - 1) mod 20 = 0 then       'show column names every now and then
        columnNames rs
      end if
      hout "<TR>"
      For i = 0 to rs.Fields.Count - 1
         temps = trim(rs(i))
         if isBlank(temps) then
            temps = "&\nbsp;"
         end if
         if isnull(temps) then
            temps = "<fontcolor=""#b0b0b0"">Null</font>"
         end if
         if len(temps) > maxCell then           
           temps = left(temp, maxCell) & " ..."
         end if
         hout "<TD>" & temps & "</TD>"
      Next
      hout "</TR>"
      rs.MoveNext
    Loop
    hout "</TABLE>"
    if lineCnt >= maxRows and not rs.eof then
        Hout "NOTE: row <b>quota</b> has been reached. Perhaps you can adjust your query to return a more specific result set.<br>"
    end if
    rs.close
  end if
end sub
'-------------------------------------
sub columnNames(rs)  ' used with DisplaySQLQuery to show column names
  dim i
  hout "<tr bgcolor=""#f0f0f0"">"
  For i = 0 to rs.Fields.Count - 1 
    hout "<th>" & rs(i).Name & "</th>"
  Next
  hout "</tr>"
end sub
'-------------------------------------
function reportFieldsSql(UserID, reportID)
' Returns report criteria fields for given report and user
  dim sql
  sql = "SELECT * FROM (Reports "
  sql = sql & " INNER JOIN ReportCriteria ON reports.ReportID = reportCriteria.ReportRef) "
  sql = sql & " INNER JOIN userFields  ON reportCriteria.ItemID = userFields.rptItemID "
  sql = sql & " WHERE reportID = " & reportID & " AND userID = " & userID
  sql = sql & " ORDER BY sequence "
  reportFieldsSql = sql     
end function
'-------------------------------------
sub initializeUserReport(userID, reportID)
' Make sure there are per-user value field records for given report
  dim sql
  sql = "INSERT INTO userFields "
  sql = sql & " SELECT " & userID & " as userID, ItemID as rptItemId, defaultVal as fldValue "
  sql = sql & " FROM ReportCriteria WHERE reportRef = " & reportID & " AND ItemID NOT IN "
  sql = sql & " (SELECT rptItemID FROM userfields "
  sql = sql & " WHERE userfields.userID = " & userID & ")"
  executeSQL sql
end sub
'-------------------------------------
function picklist1(fldName, current, theList)  ' create an HTML picklist
  ' fldName=the HTML field name, current=default item(if any), theList=comma-seperated list 
  dim i, r    
  r = "<select name=""" & fldname & """>"
  if not isBlank(current) then
    r = r & "<option>" & current
  end if
  for each i in split(theList,",")
    if trim(lcase(i)) <> trim(lcase(current) & "") then   ' dont repeat default
      r = r & "<option>" & i
    end if
  next
  r = r & "</select>"
  picklist1 = r
end function
'-------------------------------------
function inputBox(hType, fldName, fldValue, width, maxLength)
  ' Generate an HTML output box. Zero widths exclude tag
  dim r
  r = "<input type=""" & hType & """ "
  r = r & " value=""" & trim(fldValue) & """ "
  r = r & " name="""  & fldName & """ "
  if not width="0" and not width="" then
    r = r & " size=" & width
  end if
  if not maxLength="0" and not maxLength="" then
    r = r & " maxlength=" & maxLength
  end if
  inputBox = r & ">"
end function
'-------------------------------------
sub appendErr(msg)        ' Append an error message to the error list
  errMsgs = errMsgs & "<li>" & msg
end sub
'-------------------------------------
function hasErrors()      ' Returns True if any validation error messages
  hasErrors = (not isBlank(errMsgs))
end function
'-------------------------------------
function titlePadder(fldTitle, isBreak, minWidth)
' Format field title for consistent appearence.
' A future version will use HTML tables.
  dim r      ' r=result
  r = fldTitle
  if isBreak then
    if len(fldTitle) < minWidth then
      r = r & ".........................................."
      r = left(r, minWidth)
    end if
    r = "<br><tt>" & r & "</tt> "
  else
    r = space(1) & r & space(1)
  end if
  titlePadder = r 
end function
'-------------------------------------

%>


Back