1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
|
;;; "report.scm" relational-database-utility
; Copyright 1995 Aubrey Jaffer
;
;Permission to copy this software, to modify it, to redistribute it,
;to distribute modified versions, and to use it for any purpose is
;granted, subject to the following restrictions and understandings.
;
;1. Any copy made of this software must include this copyright notice
;in full.
;
;2. I have made no warrantee or representation that the operation of
;this software will be error-free, and I am under no obligation to
;provide any services, by way of maintenance, update, or otherwise.
;
;3. In conjunction with products arising from the use of this
;material, there shall be no use of my name in any advertising,
;promotional, or sales literature without prior written consent in
;each case.
;;;; Considerations for report generation:
; * columnar vs. fixed-multi-line vs. variable-multi-line
; * overflow lines within column boundaries.
; * break overflow across page?
; * Page headers and footers (need to know current/previous record-number
; and next record-number).
; * Force page break on general expression (needs next row as arg).
; * Hierachical reports.
;================================================================
(require 'format)
(require 'database-utilities)
(define (dbutil:database arg)
(cond ((procedure? arg) arg)
((string? arg) (dbutil:open-database arg))
((symbol? arg) (slib:eval arg))
(else (slib:error "can't coerce to database: " arg))))
(define (dbutil:table arg)
(cond ((procedure? arg) arg)
((and (list? arg) (= 2 (length arg)))
(((dbutil:database (car arg)) 'open-table) (cadr arg) #f))))
(define (dbutil:print-report table header reporter footer . args)
(define output-port (and (pair? args) (car args)))
(define page-height (and (pair? args) (pair? (cdr args)) (cadr args)))
(define minimum-break
(and (pair? args) (pair? (cdr args)) (pair? (cddr args)) (caddr args)))
(set! table (dbutil:table table))
((lambda (fun)
(cond ((output-port? output-port)
(fun output-port))
((string? output-port)
(call-with-output-file output-port fun))
((or (boolean? output-port) (null? output-port))
(fun (current-output-port)))
(else (slib:error "can't coerce to output-port: " arg))))
(lambda (output-port)
(set! page-height (or page-height (output-port-height output-port)))
(set! minimum-break (or minimum-break 0))
(let ((output-page 0)
(output-line 0)
(nth-newline-index
(lambda (str n)
(define len (string-length str))
(do ((i 0 (+ i 1)))
((or (zero? n) (> i len)) (+ -1 i))
(cond ((char=? #\newline (string-ref str i))
(set! n (+ -1 n)))))))
(count-newlines
(lambda (str)
(define cnt 0)
(do ((i (+ -1 (string-length str)) (+ -1 i)))
((negative? i) cnt)
(cond ((char=? #\newline (string-ref str i))
(set! cnt (+ 1 cnt)))))))
(format (let ((oformat format))
(lambda (dest fmt arg)
(cond ((not (procedure? fmt)) (oformat dest fmt arg))
((output-port? dest) (fmt dest arg))
((eq? #t dest) (fmt (current-output-port) arg))
((eq? #f dest) (call-with-output-string
(lambda (port) (fmt port arg))))
(else (oformat dest fmt arg)))))))
(define column-names (table 'column-names))
(define (do-header)
(let ((str (format #f header column-names)))
(display str output-port)
(set! output-line (count-newlines str))))
(define (do-lines str inc)
(cond
((< (+ output-line inc) page-height)
(display str output-port)
(set! output-line (+ output-line inc)))
(else ;outputting footer
(cond ((and (not (zero? minimum-break))
(> cnt (* 2 minimum-break))
(> (- page-height output-line) minimum-break))
(let ((break (nth-newline-index
str (- page-height output-line))))
(display (substring str 0 (+ 1 break) output-port))
(set! str (substring str (+ 1 break) (string-length str)))
(set! inc (- inc (- page-height output-line))))))
(format output-port footer column-names)
(display slib:form-feed output-port)
(set! output-page (+ 1 output-page))
(do-header)
(do-lines str inc))))
(do-header)
((table 'for-each-row)
(lambda (row)
(let ((str (format #f reporter row)))
(do-lines str (count-newlines str)))))
output-page))))
|