Index: openacs-4/contrib/packages/project-manager/www/task-add-edit.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/project-manager/www/Attic/task-add-edit.tcl,v diff -u -r1.26 -r1.27 --- openacs-4/contrib/packages/project-manager/www/task-add-edit.tcl 26 Feb 2004 15:15:41 -0000 1.26 +++ openacs-4/contrib/packages/project-manager/www/task-add-edit.tcl 12 Mar 2004 13:44:45 -0000 1.27 @@ -1,16 +1,3 @@ -set debug 0 - -if {[string equal $debug 1]} { - ns_log notice task add edit page - set mypage [ns_getform] - if {[string equal "" $mypage]} { - ns_log notice no form was submitted on my page - } else { - ns_log notice the following form was submitted on my page - ns_set print $mypage - } -} - ad_page_contract { Add/edit form for tasks @@ -19,9 +6,9 @@ @creation-date 2003-07-28 @cvs-id $Id$ - @return context_bar Context bar. + @return context Context bar @return title Page title. - @return num num is used as a multirow datasource to iterate over the ad_form elements + @return num A multirow datasource to iterate over the ad_form elements @return edit_p if t then we are editing. Used to show different portions of the form @param process_task_ids Specifies what process task ids were used if a process were used. We pass this on to future pages to determine things such as assignments @@ -33,8 +20,10 @@ @param task_id Specifies the item for the task (every revision of a task shares the same task_id) Also used as the key for the ad_form. For this reason, when editing, we have to pass in task_iid instead @param my_key if set, then ad_form knows that this is an edit @param task_title + @param skip_task_p Whether or not to skip this particular task. Used by processes, if t, then the task is not created @param process_id The process we're using to create this task @param use_uncertain_completion_times_p Whether or not to use PERT-style completion time uncertainty 1 = yes + @return using_process_p Lets the UI know if there is a process being used (t if so) } { my_key:integer,optional @@ -52,21 +41,32 @@ end_date:array,optional percent_complete:array,optional actual_hours_worked:array,optional + new_hours_logged:array,optional + new_description_logged:array,optional estimated_hours_work:array,optional estimated_hours_work_min:array,optional estimated_hours_work_max:array,optional dependency_type:array,optional dependency_task_id:array,optional + skip_task_p:array,optional + {using_process_p "f"} } -properties { - context_bar:onevalue + context:onevalue title:onevalue num:multirow use_uncertain_completion_times_p:onevalue edit_p:onevalue + using_process_p:onevalue } -validate { + project_exists { + if {![exists_and_not_null project_item_id] && \ + ![exists_and_not_null project_id]} { + ad_complain + } + } number_is_in_range -requires {number:integer} { # todo: make 100 a parameter if {$number < 1 || $number > 100} { @@ -79,6 +79,7 @@ } } } -errors { + project_exists {You must enter a project} number_is_in_range {Number must be between 1 and 100} percent_is_in_range {Percent completed must be between 0 and 100} } @@ -88,11 +89,15 @@ set user_id [ad_maybe_redirect_for_registration] set package_id [ad_conn package_id] + +# --------------------------------------------------------------- # # terminology +# --------------------------------------------------------------- # set project_term [parameter::get -parameter "ProjectName" -default "Project"] set task_term [parameter::get -parameter "TaskName" -default "Task"] set task_term_lower [parameter::get -parameter "taskname" -default "task"] set use_uncertain_completion_times_p [parameter::get -parameter "UseUncertainCompletionTimesP" -default "1"] +# --------------------------------------------------------------- # if {![exists_and_not_null project_id]} { @@ -110,35 +115,47 @@ set process_tasks [list] if {[exists_and_not_null process_id]} { + + # we need the ADP page to know we're using processes, so we can + # modify the interface a little bit + set using_process_p t + db_foreach get_process_tasks { } { set one_line_v($process_tid) $one_line set description_v($process_tid) $description set estimated_hours_work_v($process_tid) $estimated_hours_work set estimated_hours_work_min_v($process_tid) $estimated_hours_work_min set estimated_hours_work_max_v($process_tid) $estimated_hours_work_max - set dependency_v($process_tid) $dependency_id + set dependency_v($process_tid) $process_parent_task lappend process_tasks $process_tid } set number [llength $process_tasks] } +# --------------------------------------------------------------- # + + + # we use this to get around the lack of a hidden multiple in ad_form set task_id_pass [string map {"-" " "} $task_id] set task_id $task_id_pass + +# --------------------------------------------------------------- # +# Create a multirow: num +# --------------------------------------------------------------- # + if {![ad_form_new_p -key task_id]} { + # ----------------------------------------------------- # Editing! # ----------------------------------------------------- # create a multirow we can use to iterate # we also set the number variable for future use # ----------------------------------------------------- template::multirow create num number - # TODO:get task IDS - # - set i 1 set number 0 foreach tid $task_id { @@ -160,8 +177,8 @@ set edit_p f } -ns_log Notice "Edit_p: $edit_p number: $number " +# ------------------------------------------------------- # # The evilest hack of all time. # ------------------------------------------------------- # This is a workaround the fact that using multiple dates @@ -177,6 +194,7 @@ # the SQL function that creates the new tasks. This works. I'm # sure there must be a better way to do it, but my posting on # the forums didn't result in any other suggestions. +# ------------------------------------------------------- # if {[info exists end_date]} { @@ -197,25 +215,46 @@ for {set i 1} {$i <= $number} {incr i} { # set up date variable names set end_date_$i [list $end_date_year($i) $end_date_month($i) $end_date_day($i) {} {} {}] - ns_log Notice "End date $i: [set end_date_$i]" } } -# permissions and more +# --------------------------------------------------------------- # +# permissions and title setup, etc +# we should update the permissions to not just use package_id, so +# users can have permissions on particular tasks but not others. +# Right now it's an open system. If you have write permissions on +# the package_id, you have permissions to write anywhere +# --------------------------------------------------------------- # if {[exists_and_not_null task_id]} { + set title "Edit a $task_term_lower" - set context_bar [ad_context_bar [list "one?item_id=$project_item_id&project_id=$project_id" "One $project_term"] "Edit $task_term"] - permission::require_permission -party_id $user_id -object_id $package_id -privilege write + set context [list [list "one?item_id=$project_item_id&project_id=$project_id" "One $project_term"] "Edit $task_term"] + permission::require_permission \ + -party_id $user_id \ + -object_id $package_id \ + -privilege write + } else { + set title "Add a $task_term_lower" - set context_bar [ad_context_bar [list "one?item_id=$project_item_id&project_id=$project_id" "One $project_term"] "New $task_term"] - permission::require_permission -party_id $user_id -object_id $package_id -privilege create + set context [list [list "one?item_id=$project_item_id&project_id=$project_id" "One $project_term"] "New $task_term"] + permission::require_permission \ + -party_id $user_id \ + -object_id $package_id \ + -privilege create + } # we use this to pass through the task_ids set task_id_pass [string map {" " "-"} $task_id] + + +# --------------------------------------------------------------- # +# Begin our form +# --------------------------------------------------------------- # + ad_form -name add_edit -form { my_key:key(acs_object_id_seq) @@ -246,24 +285,26 @@ set i 1 - db_foreach get_old_tasks { } { + db_foreach get_old_tasks { *SQL* } { - set task_title_arr($i) $task_title - set description_arr($i) $description - set estimated_hours_arr($i) $estimated_hours_work - set estimated_hours_min_arr($i) $estimated_hours_work_min - set estimated_hours_max_arr($i) $estimated_hours_work_max - set end_date_arr($i) $end_date - set percent_complete_arr($i) $percent_complete - set actual_hours_worked_arr($i) $actual_hours_worked + set task_title_arr($i) $my_task_title + set description_arr($i) $my_description + set estimated_hours_arr($i) $my_estimated_work + set estimated_hours_min_arr($i) $my_estimated_work_min + set estimated_hours_max_arr($i) $my_estimated_work_max + set end_date_arr($i) $my_end_date + set percent_complete_arr($i) $my_percent_complete + set actual_hours_worked_arr($i) $my_actual_hours_worked set task_item_id_arr($i) [lindex $task_id [expr $i - 1]] + set dependency_arr($i) $my_dependency # we are not using a process set process_task_id_arr($i) "" # ----------------------------------------------------- # if we are editing the tasks, then we want to show the - # percent_complete + # percent_complete, and give the users the chance to + # log hours as well. # ----------------------------------------------------- ad_form -extend \ @@ -274,13 +315,29 @@ {html {size 4}} \ {value {$percent_complete_arr($i)}} \ ] \ - [list actual_hours_worked.$i:text \ - {label "Hours worked"} \ + [list new_hours_logged.$i:text,optional \ + {label "Hours logged"} \ {html {size 4}} \ - {value {$actual_hours_worked_arr($i)}} \ - ] - ] + ] \ + [list new_description_logged.$i:text,optional \ + {label "Description"} \ + {html {size 30}} \ + ] \ + ] + # -------------------------------------- + # we don't skip tasks when we're editing + # -------------------------------------- + ad_form -extend \ + -name add_edit \ + -form \ + [list [list \ + skip_task_p.$i:text(hidden) \ + {value {f}} \ + ] \ + ] + + incr i } } else { @@ -304,6 +361,7 @@ set actual_hours_worked_arr($i) 0 set task_item_id_arr($i) "" set process_task_id_arr($i) "" + set dependency_arr($i) "" if {[exists_and_not_null process_id]} { @@ -315,11 +373,43 @@ set estimated_hours_min_arr($i) $estimated_hours_work_min_v($ptask_id) set estimated_hours_max_arr($i) $estimated_hours_work_max_v($ptask_id) + # keeps track of what process_task_id this was based on # so we can later get info on what assignments the process # task had, and what dependencies as well set process_task_id_arr($i) $ptask_id + + # We also want to make it optional for the user whether or + # not they use any given task when they are using a + # process. Often, they will want to skips some of the + # tasks, and this gives them that option + + ad_form -extend \ + -name add_edit \ + -form \ + [list [list \ + skip_task_p.$i:text(select) \ + {label \"Skip this task?\"} \ + {options {{No f} {Yes t}}} \ + {value {f}} \ + {help_text {If you skip this task, it will not be created}} \ + ] \ + ] + + } else { + + # we never skip when there is no process being used + + ad_form -extend \ + -name add_edit \ + -form \ + [list [list \ + skip_task_p.$i:text(hidden) \ + {value {f}} \ + ] \ + ] + } # ----------------------------------------------------- @@ -334,12 +424,7 @@ {html {size 4}} \ {value {$percent_complete_arr($i)}} \ ] \ - [list actual_hours_worked.$i:text(hidden) \ - {label "Hours worked"} \ - {html {size 6}} \ - {value {$actual_hours_worked_arr($i)}} \ - ] - ] + ] } } @@ -379,18 +464,83 @@ # ---------------------------------------------------------------- # set up the tasks that can be viewed. Take out the current task. - # TODO: add in process defaults + + # --------------------------------------------------- + # set up the tasks that this task can be dependent on + # For new tasks, that includes the other new tasks + # We have to check later that the user doesn't enter + # any loops! + # --------------------------------------------------- + set dependency_options_full "" append dependency_options_full "{\"--None--\" \"\"} " - # adds in the NEW tasks as options. We have to check later to make sure - # the user doesn't enter any loops! - for {set j 1} {$j <= $number} {incr j} { - if {![string equal $i $j]} { - append dependency_options_full "{\"New Task \#$j\" \"num$j\"} " + + if {[string equal $edit_p f]} { + + # if we have a process, we need to bring in the dependencies + # from the process. + # + # We have the following: + # process_tasks: a list of all the process task ids, which act + # as keys for the other information. + # dependency_v(process_task_id) which process these tasks rely on. + # + + if {[exists_and_not_null process_id]} { + + # what is the task that the process says this task + # depends on? + set process_index [expr $i - 1] + + if {[exists_and_not_null dependency_v([lindex $process_tasks $process_index])]} { + set process_depend $dependency_v([lindex $process_tasks $process_index]) + } else { + set process_depend "XXX" + } + + # lsearch gives us the index value inside the + # process_tasks list. That corresponds to the task minus + # one. So we add one to this index, as long as the value + # is not -1 (which indicates that process_depend is not in + # the process_tasks list) + + set which_dep_task [lsearch $process_tasks $process_depend] + + if {$which_dep_task >= 0} { + incr which_dep_task + } + + if {$which_dep_task == $process_index} { + set dependency_arr($i) "num$which_dep_task" + } + } + + # now set up dependency options + + for {set j 1} {$j <= $number} {incr j} { + if {![string equal $i $j]} { + append dependency_options_full "{\"New Task \#$j\" \"num$j\"} " + } + } + } - foreach key $dependency_keys { - append dependency_options_full "{\"$key\" $dependency_options($key)} " + # for editing tasks, we skip ourselves (because depending on + # ourselves just sometimes isn't an option) + + if {[string equal $edit_p t]} { + foreach key $dependency_keys { + + # make sure we're not dependent on ourselves + + if {![string equal $task_item_id_arr($i) $dependency_options($key)]} { + append dependency_options_full "{\"$key\" $dependency_options($key)} " + } + } + } else { + foreach key $dependency_keys { + append dependency_options_full "{\"$key\" $dependency_options($key)} " + } } ad_form -extend \ @@ -400,7 +550,7 @@ [list \ task_title.$i:text \ {label "Subject \#$i"} \ - {html {size 39}} \ + {html {size 40}} \ {value {$task_title_arr($i)}} ] \ [list task_item_id.$i:text(hidden) \ @@ -434,6 +584,7 @@ dependency_task_id.$i:text(select),optional \ {label \"Dependency\"} \ {options {$dependency_options_full}} \ + {value {$dependency_arr($i)}} \ {help_text {$task_term the dependency is based on}} \ ] \ ] @@ -479,8 +630,6 @@ } -new_data { - ns_log Notice "new data" - # -------------------------------------------------------------- # each task we add in returns a task_revision_id # -------------------------------------------------------------- @@ -512,138 +661,120 @@ set p_work $estimated_hours_work($i) set p_work_min $estimated_hours_work_min($i) set p_work_max $estimated_hours_work_max($i) + set p_end_date [set end_date_$i] set p_dep_type $dependency_type($i) - set p_dep_id $dependency_task_id($i) - set parent_task_id $p_dep_id + set p_parent_task_id $dependency_task_id($i) + set p_skip_p $skip_task_p($i) - ns_log Notice "adding task: pii: $project_item_id tt:$p_task_title d:$p_description ed: end_date($i) w:$p_work m:$p_work_min mx:$p_work_max dep_type:$p_dep_type dep_id:$p_dep_id" + ns_log Notice "end date: $p_end_date" # add in the new task - set this_revision_id [project_manager::task::new \ - -project_id $project_item_id \ - -title $p_task_title \ - -description $p_description \ - -end_date [project_manager::project::util::datenvl -value [set end_date_$i] -value_if_null "" -value_if_not_null "to_timestamp('[set end_date_$i]','YYYY MM DD HH24 MI SS')"] \ - -percent_complete "0" \ - -estimated_hours_work $p_work \ - -estimated_hours_work_min $p_work_min \ - -estimated_hours_work_max $p_work_max \ - -creation_user $user_id \ - -creation_ip $peeraddr \ - -package_id $package_id - ] - set this_task_id [db_string get_task_item_id {}] - set dep_task_id($i) $this_task_id + if {[string equal $p_skip_p "f"]} { + set this_revision_id [pm::task::new \ + -project_id $project_item_id \ + -title $p_task_title \ + -description $p_description \ + -end_date $p_end_date \ + -percent_complete "0" \ + -estimated_hours_work $p_work \ + -estimated_hours_work_min $p_work_min \ + -estimated_hours_work_max $p_work_max \ + -creation_user $user_id \ + -creation_ip $peeraddr \ + -package_id $package_id] + + set this_task_id [db_string get_task_item_id {}] + + + + set dependent_task_id($i) $this_task_id - if {[exists_and_not_null p_dep_id]} { - - # if the p_dep_id contains num at the beginning, then - # the task is supposed to depend on other tasks that - # are currently being created. We have to do all sorts of - # things like make sure there are no loops, etc.. - if {[regexp {num.*} $p_dep_id]} { - regexp {num(.*)} $p_dep_id match d_parent - - # dep_parent tracks who is parent of what - set dep_parent($i) $d_parent - set dep_type($i) $p_dep_type - lappend parent_new_task $i - # we wait until after all the tasks have been created to create these - # dependencies - } else { - - # add in the new dependency - set dependency_id [db_nextval pm_task_dependency_seq] - db_dml new_dependency { *SQL* } + if {[exists_and_not_null p_parent_task_id]} { + + # if the p_parent_task_id contains num at the beginning, then + # the task is supposed to depend on other tasks that + # are currently being created. We have to wait until + # the tasks have been created + if {[regexp {num.*} $p_parent_task_id]} { + regexp {num(.*)} $p_parent_task_id match d_parent + + # dep_parent tracks who is parent of what + set dep_parent($i) $d_parent + set dep_type($i) $p_dep_type + lappend parent_new_task $i + # we wait until after all the tasks have been created to create these + # dependencies + } else { + + # add in the new dependency + pm::task::dependency_add \ + -task_item_id $this_task_id \ + -parent_id $p_parent_task_id \ + -dependency_type $p_dep_type \ + -project_item_id $project_item_id + } } - } - lappend process_task_id $process_task_ids($i) - lappend revisions $this_revision_id - lappend task_id $this_task_id + lappend process_task_id $process_task_ids($i) + lappend revisions $this_revision_id + lappend task_id $this_task_id + } } if {[exists_and_not_null parent_new_task]} { # there are tasks that depend on newly created tasks. # 1. check for loops # 2. if no loops, create dependencies - # the way we check for a loop is to follow the dependencies - # until we get to a task that has already been created. - # we make the assumption that the following is true: - # starting condition: no tasks are created - # when adding a task: no loop is created if you depend on a task already present - # therefore, if you add a task without creating a loop in the newly - # created tasks, you are safe. - - # we check that the new items don't depend on each other by following them - # if they loop more than $number times, then we have a loop - set loop_limit $number - foreach dep_task $parent_new_task { - set passes_p f + set this_task_id $dependent_task_id($dep_task) + if {[info exists dependent_task_id($dep_parent($dep_task))]} { + set parent_task_id $dependent_task_id($dep_parent($dep_task)) + set p_dep_type $dep_type($dep_task) - set my_task $dep_task - while {$loop_limit >= 0} { - - if {[exists_and_not_null dep_parent($my_task)]} { - set my_task $dep_parent($my_task) - } else { - set passes_p t - break - } - - set loop_limit [expr $loop_limit - 1] + pm::task::dependency_add \ + -task_item_id $this_task_id \ + -parent_id $parent_task_id \ + -dependency_type $p_dep_type \ + -project_item_id $project_item_id } - if {[string equal $passes_p t]} { - # add in the new dependency - set dependency_id [db_nextval pm_task_dependency_seq] - set this_task_id $dep_task_id($dep_task) - set parent_task_id $dep_task_id($dep_parent($dep_task)) - set p_dep_type $dep_type($dep_task) - db_dml new_dependency { *SQL* } - } else { - ns_log Notice "PM: Adding in dependency failed for $dep_task_id($dep_task) due to looping" - } - } - - } - ns_log Notice "Project_item_id $project_item_id" - project_manager::project::compute_parent_status $project_item_id + pm::project::compute_parent_status $project_item_id } -edit_data { - ns_log Notice "edit_data" + set timestamp_ansi [db_string get_today " + SELECT + to_char(current_date, 'YYYY-MM-DD')"] # -------------------------------------------------------------- # each task we edit returns a task_revision_id # -------------------------------------------------------------- for {set i 1} {$i <= $number} {incr i} { + if {![exists_and_not_null end_date_$i]} { + set end_date_$i "{} {} {} {} {} {}" + } + if {![exists_and_not_null estimated_hours_work($i)]} { set estimated_hours_work($i) [expr .5 * ($estimated_hours_work_max($i) - $estimated_hours_work_min($i)) + $estimated_hours_work_min($i)] } - + if {![exists_and_not_null estimated_hours_work_min($i)]} { - set estimated_hours_work_min($i) $estimated_hours_work($i) + set estimated_hours_work_min($i) $estimated_hours_work($i) } - + if {![exists_and_not_null estimated_hours_work_max($i)]} { set estimated_hours_work_max($i) $estimated_hours_work($i) } - if {![exists_and_not_null end_date_$i]} { - set end_date_$i "{} {} {} {} {} {}" - } - # set up variables, pulling from arrays set p_task_item_id $task_item_id($i) set p_task_title $task_title($i) @@ -652,30 +783,76 @@ set p_work $estimated_hours_work($i) set p_work_min $estimated_hours_work_min($i) set p_work_max $estimated_hours_work_max($i) - set p_hours $actual_hours_worked($i) + set p_parent_task_id $dependency_task_id($i) - # do the actual edit - set this_revision_id [db_exec_plsql new_task_revision { }] + set p_new_hours $new_hours_logged($i) + set p_new_description $new_description_logged($i) - ns_log Notice "Added in $this_revision_id" + if {![empty_string_p $p_new_hours] && \ + $p_new_hours > 0} { + + set logger_project [pm::project::get_logger_project \ + -project_item_id $project_item_id] + set variable_id [logger::variable::get_default_variable_id] + + # add in the new log entry + set p_hours [pm::project::log_hours \ + -logger_project_id $logger_project \ + -variable_id $variable_id \ + -value $p_new_hours \ + -description $p_new_description \ + -timestamp_ansi $timestamp_ansi \ + -task_item_id $p_task_item_id \ + -project_item_id $project_item_id \ + -update_status_p f] + if {[string equal p_hours -1]} { + ns_log Error "Error in logging hours in task-add-edit" + set p_hours 0 + } + } else { + set p_hours [pm::task::update_hours \ + -task_item_id $p_task_item_id \ + -update_tasks_p f] + } + + # do the actual edit + set this_revision_id [pm::task::edit \ + -task_item_id $p_task_item_id \ + -project_item_id $project_item_id \ + -title $p_task_title \ + -description $p_description \ + -end_date "[set end_date_[set i]]" \ + -percent_complete $p_percent \ + -estimated_hours_work $p_work \ + -estimated_hours_work_min $p_work_min \ + -estimated_hours_work_max $p_work_max \ + -actual_hours_worked $p_hours \ + -update_user $user_id \ + -update_ip $peeraddr \ + -package_id $package_id] + + lappend revisions $this_revision_id - ns_log Notice "in new: task_id $task_id" + pm::task::dependency_delete_all \ + -task_item_id $p_task_item_id - # BUG: we need to make sure we take care of deleting dependencies - # if unchecked, adding dependencies, etc.. + if {![empty_string_p $p_parent_task_id]} { + pm::task::dependency_add \ + -task_item_id $p_task_item_id \ + -parent_id $p_parent_task_id \ + -dependency_type jfdsafesa\ + -project_item_id $project_item_id + } + } - project_manager::project::compute_parent_status $project_item_id + pm::project::compute_parent_status $project_item_id - ns_log Notice "computed parent pre redirect" - } -after_submit { - ns_log Notice "TASK_ID: $task_id" - ad_returnredirect "task-assign-add-edit?[export_vars -url {project_item_id process_task_id:multiple revisions:multiple task_id:multiple}]" ad_script_abort