#! /bin/env tclsh
package require {ycl dict}
namespace import [yclprefix]::dict::getdefault
package require {ycl iapi com kforce util}
namespace import [yclprefix]::iapi::com::kforce::util::clean
package require {ycl parser scripted prototype}
namespace import [yclprefix]::parser
package require {ycl proc}
namespace import [yclprefix]::proc::checkargs
package require {ycl shelf shelf}
[yclprefix] shelf shelf spawn [namespace current]
[namespace current] subcmd clean
proc hours {_ value} {
upvar 0 [$_ namespace]::parsed parsed
set hours [getdefault $parsed hours {}]
set row [lindex $hours end]
lappend row $value
dict set parsed hours [lreplace $hours end end $row]
return $row
}
[namespace current] method hours
if 0 {
proc hours {node pattern} {
set table [list]
set rowheaders {Hours {Regular IC} {Daily Total}}
set node [$node selectNodes {//*[contains(text(),$pattern)]/../..}]
if {$node eq {} } {
raise -code error "Hours should not be parsed on a timecard with no hours"
return
}
for {set i 0} {$i<3} {incr i} {
set row [list]
foreach child [$node childNodes] {
set values [list]
foreach text [$child selectNodes */text()] {
lappend values [$text asText]
}
lappend row $values
}
lappend table $row
$node nextSibling node
}
return $table
}
}
variable doc::init {
args {
_ {
description {
This instance .
}
}
meta {
description {
metadata for the timecard
}
}
}
}
proc init {_ args} {
checkargs [set doc::[namespace tail [lindex [info level 0] 0]]] {*}$args
$_ $ meta $meta
return $_
}
[namespace current] method init
proc parse _ {
upvar 0 [$_ namespace]::timecard parsed
set parsed {}
[parser scripted prototype spawn [$_ namespace]::parser1] init states [$_ $ states]
$_ subcmd [$_ namespace]::parser1
$_ parser1 $ parsed {}
$_ parser1 subcmd timecard $_
$_ parser1 method parsed [namespace current]::parsed
$_ parser1 method hours [namespace current]::hours
$_ parser1 eval [list namespace import [namespace current]::getdefault]
dom parse -html [$_ $ data] html
$html documentElement root
$root normalize
set nodes [$root selectNodes {//text()[
not(ancestor::embed)
and not(ancestor::script)
and not(ancestor::style)
and not(ancestor::noscript)
and not(ancestor::source)
and not(ancestor::svg)
and not(ancestor::track)
and not(ancestor::video)
]}]
foreach node $nodes[set nodes {}] {
lappend nodes [string trim [$node asText]]
}
$_ parser1 parse [coroutine [$_ namespace]::nodescoro ::apply [list nodes {
yield [info coroutine]
foreach node $nodes {
yield $node
}
} [namespace current]] $nodes]
set parsed [$_ parser1 $ parsed]
set parsed [$_ clean $parsed]
return $parsed
}
[namespace current] method parse
proc parsed {_ args} {
upvar 0 [$_ namespace]::parsed parsed
if {[llength $args] > 1} {
dict set parsed {*}$args
} elseif {[llength $args] == 1} {
dict get $parsed [lindex $args 0]
} else {
return $parsed
}
}
[namespace current] method parsed
variable states {
{Consultant Name:} {}
expr 1 {
dict set [$_ $.locate parsed] consultant $value
}
{Employee ID:} {}
{
$_ parsed employee_id $value
}
{Week Ending:} {}
{
$_ parsed week $value
}
{Client:} {}
{
$_ parsed client $value
}
{Fax Number:} {}
{
$_ parsed fax $value
}
{
#warning: In this case the value comes before the value name
$_ parsed assignment $value
}
{Assignment ID:} {}
{Client Location:} {}
{
upvar 0 [$_ namespace]::parsed parsed
dict lappend parsed location $value
}
match {Job Title:} {
} {
upvar 0 [$_ namespace]::parsed parsed
dict lappend parsed location $value
}
{
$_ parsed title $value
}
{Cost Center:} {}
{
$_ parsed cost_center $value
}
{Department:} {}
{
$_ parsed department $value
}
{Hiring Manager:} {}
{
$_ parsed manager $value
}
{Project:} {}
{
$_ parsed project $value
}
{Purchase Order:} {}
{
$_ parsed purchase_order $value
}
equal {Hours} {} {
#Incomplete timecard. We're finished.
$_ $ sindex [llength [$_ $ states]]
}
{Mon*} {}
{Tue*} {}
{Wed*} {}
{Thu*} {}
{Fri*} {}
{Sat*} {}
{Sun*} {}
{Total} {}
{Regular IC} {
upvar 0 [$_ namespace]::repeat repeat
set repeat 8
upvar 0 [$_ namespace]::parsed parsed
set hours [getdefault $parsed hours {}]
lappend hours {}
dict set parsed hours $hours
}
expr {[string is double $value] || $value eq {}} {
upvar 0 [$_ namespace]::repeat repeat
$_ hours $value
if {[incr repeat -1]} {
$_ repeat
}
}
{Daily Total:} {
upvar 0 [$_ namespace]::repeat repeat
set repeat 8
upvar 0 [$_ namespace]::parsed parsed
set hours [dict get $parsed hours]
lappend hours {}
dict set parsed hours $hours
}
expr {[string is double $value] || $value eq {}} {
upvar 0 [$_ namespace]::repeat repeat
$_ hours $value
if {[incr repeat -1]} {
$_ repeat
}
}
{Special Timecard Notes} {}
{
$_ parsed notes $value
}
{Execution or approval of this form by the client constitutes*} {}
{
#warning: In this case the value comes before the value name
$_ parsed {user role} $value
}
equal {User Role:} {} {
set status [dict get [$_ timecard $ meta] status]
if {$status eq {Completed}} {
#return -code error [
#list {A completed timecard should have a User Role} [$_ timecard $ meta]]
# A timecard that is completed but that has no user role was
# rejected. We're finished
set meta [$_ timecard $ meta]
dict set meta status Rejected
$_ timecard $ meta $meta
$_ $ sindex [llength [$_ $ states]]
} else {
#If there's no User Role, we're finished with this timecard
$_ $ sindex [llength [$_ $ states]]
}
}
{
#warning: In this case the value comes before the value name
$_ parsed {action type} $value
}
{Action Type:} {}
{
#warning: In this case the value comes before the value name
$_ parsed {action by} $value
}
{Action By:} {}
{
#warning: In this case the value comes before the value name
$_ parsed reference $value
}
{Reference:} {}
{
#warning: In this case the value comes before the value name
$_ parsed {action datetime} $value
}
{Action Date/Time:} {}
{
#warning: In this case the value comes before the value name
$_ parsed {user ip address} $value
}
{User IP Address:} {}
{For Office use only :} {}
{
$_ parsed {for office use only} $value
}
{
$_ parsed {form revision} $value
}
}