Tech News Back Issues Issue: 100604
The Report Process
In the last article we surveyed some of the basic features of an Omnis Studio Report Class, focusing on the layout side of the Class and comparing and contrasting Report Classes with Window Classes. This time we will detail the process that Omnis Studio follows when it generates a report automatically from a native Omnis database with no programming intervention. We do this not because the native Omnis database is "superior" in some way, but because this process makes a good model for us to follow when building our own reports where we want to take more control of things. In future articles, we will explore ways in which we can override or enhance certain of these built-in features.
An "automatic" report in Omnis Studio is a "black box" in many ways. The technologies used within the process are not exposed to us, so we must do a certain amount of "educated guessing" to understand what happens between execution of the Print report command and our first glimpse of the finished report. What follows is my interpretation of the process...
Going With The Flow
Here is the basic flow of the Omnis Studio report process. For maximum use of features, let us assume that we are running a complex report that includes the use of a search to limit records, a number of sort levels, subtotaling on some of these levels to generate aggregate values for multiple variables within the report, totals at the end of the report and a header and a footer on each page. We will detail many of the pieces used to manage all of this information later in the article.
First, Omnis Studio goes through a setup phase. There are a number of memory partitions that must be established to handle sub-processes from record sorting to page layout. While no such constructs are mentioned in the Omnis Studio documentation, a thoughtful review of the report process indicates that such things must be created - so we will give them names to aid in discussing these inner workings.
Next, Omnis Studio must determine which records will be included in the report and in what order they are to be presented.
After this has been done, Omnis Studio proceeds to process these records one at a time, adding a Record Section to the current page image. At the same time, aggregate values are accumulated for use in subtotal sections. In fact, Omnis Studio must check whether one or more of the reports Subtotal Sections needs to be added to the current page as well as checking to see whether it is time to begin a new page.
As each page is filled, Omnis Studio closes the page and releases it to the current printing device. That is, of course, unless some special feature in the report requires that the filled page be held for later update as more pages are added. We will consider one such feature here.
After the last record has been processed, Omnis Studio places the last group of Subtotal Sections onto the current page. It then does the same thing with the Totals section.
When the End of Report Section is encountered, Omnis Studio releases any remaining pages to the printing device (after performing whatever action any earlier pages require before release). It then performs various housekeeping chores, releasing any memory areas it had used during the report process.
This is the short version. Now let's go into a little more detail about some of these mysterious stages...
Blind Man's Buffers
The first thing Omnis Studio must do when called upon to print a report is to set up certain workspaces in the computer's RAM. The technical term for such a workspace is a buffer. Each buffer serves a different specific purpose in the overall process. While these buffers are not mentioned in the Omnis Studio documentation, we know that they must exist because of the work that must be done to organize a report. So let us give names to them and then consider how they may be structured and manipulated. This exercise will help us understand how to emulate these features should we ever need to augment or override the Omnis report process with our own code.
The first of these buffers that Omnis Studio must allocate is what we will call the Sort Buffer. When we set up the Sort Fields window for multiple levels of sorting within a report, we are specifying the structure of this buffer. As part of the initialization of a report, Omnis Studio uses the variable names we provide in the Sort Field information to define the Sort Buffer. This is essentially a list in RAM used to hold images of the records for a report and to put those images into the proper order. Certainly the Omnis datafile can contain indexes for maintaining scanning order within the database, but each index links a single value with a record. True, that value may be a composite from multiple columns, but it is still a single value and does not offer the flexibility of the nine sort levels of a report.
The Sort Buffer can have up to nine columns of ordering values and each column can be independently sorted in the ascending or descending direction. In addition to these columns is a column for the Record Sequencing Number (RSN) for each record. Whether or not a Sequence variable is included in a File Class, each record in that File still has a unique RSN value that Omnis Studio uses to uniquely identify it. This is the most important column in the Sort Buffer, because the purpose of the Sort Buffer is to get the Main File RSN values into the proper order for the printing of the report.
Another buffer that Omnis Studio must set up in the initialization phase is what we will call the Subtotal or Aggregation Buffer. This is a structured collection of variables used to accumulate count, total, minimum and maximum values for specific variables at each of nine potential subtotal levels as the report proceeds. We can also retrieve an average value at a subtotal break, but I suspect that this is derived from the count and total values rather than maintained separately (but then, I don't really know this for certain - that's just how I would have done it). Other statistics, such as the median value, can also be determined from these basic building blocks. (If we want to also track the sum of squares for determining standard deviation, etc., we must manage such values "manually" - but that is a subject for another article...)
During the initialization phase of a report, Omnis Studio seems to examine the Subtotal and Totals sections of the Report Class to determine which variables are represented by fields with a totalmode setting other than kTMNone. It then adds a slot for that variable in the Aggregation Buffer. Omnis Studio appears to track all of these cumulative values for each such variable. If only a field for kTMTotal is given, the $count, $minimum, $maximum and $average values are also available through Notation. But if no aggregate fields occur for a variable, its notational aggregate properties remain empty.
A third buffer that Omnis Studio sets up at this point is a page layout buffer we will call the Virtual Page. Omnis Studio does not send each element of a report directly to the printing device. Rather, it lays out an enitre page and then sends the completed page description to the printing device. So a memory buffer must be needed for the accumulation of this information. Under some circumstances, such as when the Page Count object is used in a report, Omnis Studio must hold nearly-completed pages in memory and only release them when they can be finalized. In the case of the Page Count object, the entire report must be held in RAM until it is completed so that the total number of pages can be communicated back to each page.
Once these buffers have been established, Omnis Studio can then move on to the next phase of the report.
Gather Ye Records...
That next phase is the gathering of record images for sorting purposes. Omnis Studio must scan the Main File for the report to populate the Sort Buffer. If a search is invoked along with the report, then only those records that meet the search criteria are represented in the Sort Buffer. Only the values of variables declared in the Sort Fields window, plus the Main File RSN, are gathered at this time. If a variable was specified as Upper case in the Sort Fields window, I suspect that the values for that variable are converted to upper case on addition to the Sort Buffer.
Once all the record images have been read into the Sort Buffer, the list is then sorted on the criteria (ascending or descending) given in the Sort Fields window. The record images held there are now in the proper order for the report, so Omnis Studio goes to the top of this list and reads those records a second time, but this time the entire record is used...
A Matter Of Record Sections
The processing of records through the Record Section is a pivotal part of the report process. After sorting, each record is presented to the Record Section. A number of things happen while the Record Section is in charge. Each field is populated in its turn with either the value of its associated variable or by evaluating its calculation expression. The layout description of each field is then positioned within a Virtual Record Section that will be positioned on the Virtual Page in its turn. But this is just the first step...
A Place For Everything...
As each Record Section is filled, it is added to the Virtual Page. The layout of record sections on the Virtual Page continues until the current page is filled. In the simplest scenario, this page is then closed and released to the printing device. If the report is a label report, then the Record Section images are placed in the appropriate position within the label grid, otherwise they are simply stacked one below the previous one. The property values assigned to the Report Class ($islabel, $labelcount, $labelwidth, etc.) determine how the Record Section images are arranged.
Record Sections are placed one after the other on the same page unless a page break is called for. In the automatic reports that we are following in this article, this occurs either when a section does not fit completely onto the current page (in which case, that section is split between the current and the next page), when the pagemode property value of the section requires it (for kNewpage, kTestspace or kFitonpage values of this property) or when a Sort Field requires it. Before placing the Record Section on the Virtual Page, Omnis Studio consults the Sort Buffer to determine whether the value of any sort field with a newpage property value set to kTrue has changed sufficiently from the previous record to require a page break. So the Sort Buffer's job is not fulfilled by simply sorting record images - it continues to take an active role in the processing of records. (We will see that it has yet another job a little further in the process.)
As new pages are required, Omnis Studio first lays out the page border, background picture and pattern, etc. and the Page Header Section for that page. It also reserves space for the Page Footer Section. The Footer is not laid out until the page is otherwise completed and about to be released to the printing device. In an automatic report the Header and Footer will remain the same size from page to page. In a future article we may explore how we can deal with these items more dynamically.
If the repeat factor for the report is greater than one, multiple copies of this image are placed on the Virtual Page. If the repeat factor is zero, the Record Section image is not added to the Virtual Page. This creates what we will call a summary report - one with only Subtotal and Totals Sections. But the rest of the Record Section processing does occur whether or not the Record Section image is mounted on the Virtual Page...
The Bucket Brigade
While Omnis Studio processes each Record Section, it also determines which variable values (which can be either File Class or Instance variables) must be passed to the Aggregation Buffer. Each such value is then accumulated into each of the associated "buckets" for the various Subtotal levels and tested against the current minimum and maximum values for each Subtotal level. The count bucket for each Subtotal level is also incremented by one. In a complex report, this is a lot of "take one down and pass it around" processing for our nine bottles of subtotal accumulation (plus one for the Grand Total - and times four or five for the various types of aggregation)!
When a Subtotal Section at a specific level is triggered, these values are then available for use by fields with non-zero totalmode values in that Section and for notation strings that access the various aggregate values of the variables represented in the Aggregation Buffer. After that Subtotal Section is completed, the buckets for all the values at the Subtotal Level (and any lower levels) are emptied to begin accumulating anew for the next subtotal grouping at the level.
But just how does Omnis Studio know when to build a Subtotal Section?
Clock Watchers Waiting To Take A Break
Before a Record Section image is placed on the Virtual Page, Omnis Studio tests each variable represented in the Sort Buffer against the value of the previous record to see whether the subtotal triggering criteria have been met for any Subtotal Level. Only Sort Fields with their subtotals property value set to kTrue need to be tested. If the criteria for one or more Subtotal Level breaks is met, those Subtotal Sections are processed in order from highest to lowest level number.
As the content of a Subtotal Section is being compiled, the Aggregation Buffer values for the corresponding Subtotal Level become available. This is the only time we have access to these values! They are not exposed to us in any way I have been able to discover while they are being accumulated. They are not even available from the $print method of that section! So if we need to retain them for use elsewhere in the report (such as building a list of subtotals for a graph or a summation list in our Totals Section), we need to use fields on the section layout to assign these values to other variables (and only hash variables seem to have the stability or endurance for this purpose in my testing so far...).
For labeling and review purposes in a Subtotal Section, Omnis Studio "rolls back" the values from the most recent Record Section by default during the processing of a Subtotal Section. This allows us to label items in the section like "Totals for ..." with appropriate values. Remember that a Subtotal Section is only triggered by the current record moving beyond the sort value on which the Subtotal Level depends. This rollback feature is extremely important in helping us create understandable reports. Of course, we can override this feature for specific variables by setting the noreload property value of a field representing that variable to kFalse, but we usually shouldn't bother...
End Of The Line
As Omnis Studio cycles through the Sort Buffer, it has no problem detecting when it has reached the last record for the report. When this occurs, a number of things must happen: The final collection of Subtotal Sections must be processed and added to the Virtual Page, the Totals Section must also be processed and added to the Virtual Page, the final page and all pending pages must be released to the printing device and the process buffers used during the report must be cleared and the memory they used deallocated.
These last two items are the responsibility of the End of Report Section. This section does not just signify the "bottom" of the report layout. It signals Omnis Studio that there is no more report processing to be done. This means that it is now time for the remainder of the report to be passed to the printing device and for the allocation of the report buffers to be reversed.
In older generations of Omnis, it was possible to not include an End of Report Section. People who did not understand the importance of this section simply failed to include it or decided to leave it off because it "didnt' do anything" in their view. These same people did notice, however, that when a second report was printed, the last page of the first report magically appeared in the printer. And occasionally, after the printing of a number of reports, their clients began getting "Out of memory" errors and had to restart Omnis. These were not "bugs", but simply the result of not using the End of Report Section to perform its janitorial duties.
In Omnis Studio, the Record and End of Report Sections are not optional (they never were, actually) or removable. They are the first objects automatically included in a new Report Class (ident numbers 1001 and 1002 respectively) and are mandatory for any report.
End Of Report Process Article Section...
I hope you found this review of the report process informative. I realize there are no code listings and no screen shots in this article, but we are just setting the ground work for future articles.
In the next article we will briefly look at list-based reports and then begin to examine how we can supplement the built-in features of Omnis Studio reports to deal with more complex tasks and data sets than Omnis can in "automatic" mode.
Copyright of the text and images herein remains with the respective
author. No part of this newsletter may be reproduced, transmitted,
stored in a retrieval system or translated into any language in any
form by any means without the written permission of the author or
Omnis® and Omnis Studio® are registered trademarks, and Omnis 7 is a trademark of Raining Data UK Ltd. Other products mentioned are trademarks or registered trademarks of their corporations. All rights reserved.