Datasheet interface

Previous: How I'm writing pages out ] [ Top: To-do manager ] [ Next: Interpreting the results that wftk sends back ]

For working with datasheets, I'm defining two procedures: datasheet_getvalue goes to the datasheet associated with a process and retrieves a data value, and datasheet_setvalue sets the named value. (And of course does other XML maintenance, like create the file if necessary.)

These functions will be used in the task/process show and update functions.

There are two global settings we'll need:
set todomgr_xmltools "/usr/local/AOLserver/vivtek/pages/xmltools"
set todomgr_datasheets "/usr/local/AOLserver/vivtek/pages/todomgr/datasheets"

See Definition of datasheet_getvalue
See Definition of datasheet_setvalue
These functions, by the way, are based on my command-line XML utilities to simplify the Tcl end. The xmltools are written on James Clark's expat, a nice, solid XML parser.

These functions are in turn called by datasheet_showdata, which, given a process ID and optional task ID, iterates down the list of data to display data attached to the named task or to the process if the task is not supplied. The same function is used to generate either an editing form or a simple static view. The output is organized into table rows of two columns; this output is assumed to be assigned to a tag, so that the table element itself will be supplied elsewhere (usually on the format page.)
See Definition of datasheet_showdata

In addition to the basic functions, I'm defining one URL handler, setvalue; setvalue will take a process and task ID and information about the value or values, and create it or replace it using datasheet_setvalue. The corresponding use of datasheet_getvalue is kind of here and there throughout the show screens.
ns_register_proc GET  $todomgr_root/setvalue setvalue
ns_register_proc POST $todomgr_root/setvalue setvalue
See URL handler setvalue

URL handler setvalue
This proc handles value setting operations by making calls to datasheet_setvalue. Obviously this isn't the most efficient way of handling things, but it works, and it's extremely modular, so it should be easy to maintain. In the interests of scalability, we'll want to investigate more integrated approaches to datasheet maintenance.
proc setvalue {conn ignore} {
   set form [ns_conn form $conn]
   if {$form == ""} { 
      set tags(title) "No parameters given"
      set tags(body) "You can't set a value without giving the value."
      return [todomgr_pageout $conn message.html]
   set process [ns_set get $form process]
   set task [ns_set get $form task]
   set newname [ns_set get $form newname]
   if [string compare "" $newname] {
      datasheet_setvalue $process $task $newname [ns_set get $form type] ""
      return [ns_returnredirect $conn [ns_set get $form back]

   set size [ns_set size $form]
   for {set i 0} {$i < $size} {incr i} {
      if {-1 < [lsearch {process task back newname type} [ns_set key $form $i]]} { continue }

      datasheet_setvalue $process $task [ns_set key $form $i] "" [ns_set value $form $i]
   return [ns_returnredirect $conn [ns_set get $form back]]

Definition of datasheet_getvalue
The getvalue procedure must not only retrieve the value, but format it as an appropriate field as well so that we can include it into a form for update. The active parameter governs whether the caller wants a form field or just a value; the return from the proc is a list consisting of name, type, and the value or field insert.

I have the feeling that paragraph didn't make a lot of sense, so I'll probably come back and rewrite it later.

At any rate, the data values attached to a task or directly to the process are accessed not by name, but by number. This allows a form to be built by scanning up until a blank return. The return value is a list consisting of the data item's: id, type, value, the HTML needed to edit the value, and a full list of attributes attached to the element.

Note the gyrations required to detect an empty data element (i.e. a data value with a value of the empty string.) Sheesh.
proc datasheet_getvalue {process task number {active 0}} {
   global todomgr_datasheets
   global todomgr_xmltools
   set datasheet "$todomgr_datasheets/$process"
   if ![file exists $datasheet] return ""

   set loc datasheet
   if [string compare $task ""] { append loc ".task\[$task\]" }
   append loc ".data($number)"

   set data [exec $todomgr_xmltools/xmlsnip $loc $datasheet]
   if {0 == [regexp ^<(\[^<\]*)> $data tag bits]} { return "" }
   if  [string match */ $bits] {
      set data ""
      regsub /$ $bits "" bits
   } else {
      regsub ^<\[^<\]*> $data "" data
      regsub <\[^>\]*>$ $data "" data
   set fields [list]
   set id ""
   set type ""
   set bits [split [join [lrange [split $bits] 1 end]] =]
   set name [lindex $bits 0]
   foreach bit [lrange $bits 1 end] {
      set bit [split $bit \"]
      lappend fields [list $name [lindex $bit 1]]
      switch $name {
         id { set id [lindex $bit 1] }
         type { set type [lindex $bit 1] }
      set name [string trim [lindex $bit 2]]
   switch $type {
      string { set html "<input name=\"$id\" value=\"$data\">" }
      text { set html "<textarea name=\"$id\" rows=5 cols=40>$data</textarea>" }
      default { set html "<input name=\"$id\" value=\"$data\">" }
   return [list $id $type $data $html $fields]

Definition of datasheet_showdata
The datasheet_showdata is used from the task and process screens to build the form necessary to edit attached data (or simply to view it otherwise). If the action is edit, then it needs the back parameter to give to the setvalue URL handler as a redirect target.
proc datasheet_showdata {action back process task} {
   set retval ""
   set i 0

   while (1) {
      set d [datasheet_getvalue $process $task $i]
      incr i
      if ![string compare "" [lindex $d 0]] { break }
      if {$action == "edit"} {
         append retval "<tr><td>[lindex $d 0]:</td><td>[lindex $d 3]</td></tr>\n"
      } else {
         append retval "<tr><td>[lindex $d 0]:</td><td>[lindex $d 2]</td></tr>\n"

   if {$action == "edit" && $retval != ""} {
      set retval "<form action=setvalue method=post>\n$retval"
      append retval "<input type=hidden name=process value=\"$process\">\n"
      append retval "<input type=hidden name=task value=\"$task\">\n"
      append retval "<input type=hidden name=back value=\"$back\">\n"
      append retval "<tr><td colspan=2><center>"
      append retval "<input type=submit value=\"Update values\"></center></td></tr>\n"
      append retval "</form>\n"

   return $retval

Definition of datasheet_setvalue
To set the value, we have to

Not too onerous, eh?
proc datasheet_setvalue {process task name type value} {
   if ![string compare "" $process] { return }
   regsub -all "\"" $name ' name
   global todomgr_datasheets
   global todomgr_xmltools
   set datasheet "$todomgr_datasheets/$process"
   set loc datasheet
   if [string compare $task ""] { append loc ".task\[$task\]" }

   if ![file exists $datasheet] {
      exec $todomgr_xmltools/xmlcreate datasheet > $datasheet
   if [string compare $task ""] {
      set taskloc [exec $todomgr_xmltools/xmlsnip -otl datasheet.task\[$task\] $datasheet]
      if ![string compare $taskloc ""] {
         exec mv $datasheet $datasheet.~
         set pipe [open "|$todomgr_xmltools/xmlinsert aftercontent datasheet $datasheet.~ > $datasheet" w]
         puts $pipe ""
         puts $pipe ""
         close $pipe
   set dataloc [exec $todomgr_xmltools/xmlsnip -otl $\[$name\] $datasheet]
   if [string compare $dataloc ""] {
      exec mv $datasheet $datasheet.~
      regsub -all " " $name "\\ " n
      set cmd "|$todomgr_xmltools/xmlreplace -m $\[$n\] $datasheet.~ > $datasheet"
      set pipe [open $cmd w]
      puts -nonewline $pipe $value
      close $pipe
   } else {
      exec mv $datasheet $datasheet.~
      set cmd "|$todomgr_xmltools/xmlinsert aftercontent $loc $datasheet.~ > $datasheet"
      set pipe [open $cmd w]
      puts $pipe "$value"
      close $pipe
Previous: How I'm writing pages out ] [ Top: To-do manager ] [ Next: Interpreting the results that wftk sends back ]

This code and documentation are released under the terms of the GNU license. They are additionally copyright (c) 2000, Vivtek. All rights reserved except those explicitly granted under the terms of the GNU license.