Index: openacs-4/contrib/packages/project-manager/tcl/project-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/project-manager/tcl/Attic/project-procs.tcl,v
diff -u -r1.5 -r1.6
--- openacs-4/contrib/packages/project-manager/tcl/project-procs.tcl 26 Feb 2004 15:15:41 -0000 1.5
+++ openacs-4/contrib/packages/project-manager/tcl/project-procs.tcl 12 Mar 2004 13:44:43 -0000 1.6
@@ -10,27 +10,164 @@
}
-namespace eval project_manager::project {}
+namespace eval pm::project {}
-ad_proc -public project_manager::project::default_status_open {
+
+ad_proc -public pm::project::get_project_id {
+ -project_item_id:required
} {
+ Returns the live project_id when give the project_item_id
+
+ @author Jade Rubick (jader@bread.com)
+ @creation-date 2004-02-19
+
+ @param project_item_id The item_id for the project
+
+ @return project_id
+
+ @error
+} {
+ set return_val [db_string get_project_id { }]
+
+ return $return_val
+}
+
+
+
+ad_proc -public pm::project::get_project_item_id {
+ -project_id:required
+} {
+ Returns the item_id for a project when given the project_id
+ (a revision id)
+
+ @author Jade Rubick (jader@bread.com)
+ @creation-date 2004-02-19
+
+ @param project_id
+
+ @return project_item_id
+
+ @error
+} {
+ set return_val [db_string get_project_item { }]
+
+ return $return_val
+}
+
+
+
+ad_proc -public pm::project::default_status_open {} {
Returns the default status value for open projects
} {
set return_val [db_string get_default_status_open { }]
return $return_val
}
-ad_proc -public project_manager::project::default_status_closed {
-} {
+
+
+ad_proc -public pm::project::default_status_closed {} {
Returns the default status value for closed projects
} {
set return_val [db_string get_default_status_closed { }]
return $return_val
}
-ad_proc -public project_manager::project::new {
+
+
+ad_proc -private pm::project::log_hours {
+ {-entry_id ""}
+ -logger_project_id:required
+ -variable_id:required
+ -value:required
+ -timestamp_ansi:required
+ {-description ""}
+ {-task_item_id ""}
+ {-project_item_id ""}
+ {-update_status_p "t"}
+} {
+ Adds a logger entry to a project. If task_item_id is passed
+ in, also links it up with that task, and updates the task
+ hours.
+
+ @author Jade Rubick (jader@bread.com)
+ @creation-date 2004-03-05
+
+ @param entry_id If passed in, determines the entry_id for the
+ newly logged entry
+
+ @param logger_project_id
+
+ @param variable_id
+
+ @param value
+
+ @param timestamp_ansi Timestamp in ANSI format YYYY-MM-DD
+
+ @param description
+
+ @param task_item_id
+
+ @param project_item_id If the task_item_id is passed in, the
+ project_item_id needs to be passed in as well
+
+ @param update_status_p If t, then updates the database for the
+ task and project so that the denormalized values are updated.
+ This can be set to f when editing a task, because these things
+ are done later anyway.
+
+ @return 0 if there no task it is logged against, otherwise returns
+ the total number of hours logged to that task
+
+ @error returns -1 if there is an error in pm::task::update_hours
+ If a task_id is passed in, essentially passes along any errors
+ from pm::task::update_hours
+} {
+ set returnval 0
+
+ # get a new entry_id if it's not passed in (like it would be from
+ # a page that was using ad_form)
+ if {[empty_string_p $entry_id]} {
+ set entry_id [db_nextval acs_object_id_seq]
+ }
+
+ # add in the new entry
+ logger::entry::new -entry_id $entry_id \
+ -project_id $logger_project_id \
+ -variable_id $variable_id \
+ -value $value \
+ -time_stamp $timestamp_ansi \
+ -description $description
+
+ # if we have a pm_task_id, then we need to note that this
+ # entry is logged to a particular task.
+ if {[exists_and_not_null task_item_id]} {
+ db_dml add_logger_map "
+ INSERT INTO
+ pm_task_logger_proj_map
+ (task_item_id,
+ logger_entry)
+ VALUES
+ (:task_item_id,
+ :entry_id)
+ "
+
+ set returnval [pm::task::update_hours \
+ -task_item_id $task_item_id \
+ -update_tasks_p $update_status_p]
+
+ if {[string equal $update_status_p "t"]} {
+ pm::project::compute_status $project_item_id
+ }
+ }
+
+ return $returnval
+}
+
+
+
+ad_proc -public pm::project::new {
-project_name:required
{-project_code ""}
{-parent_id ""}
@@ -48,24 +185,176 @@
-creation_ip:required
-package_id:required
} {
+ Creates a new project
- set return_val [db_exec_plsql new_project_item { *SQL }]
+ @author Jade Rubick (jader@bread.com)
+
+ @param project_name
- return $return_val
+ @error
+} {
+
+ # if the project is ongoing, there is no end date
+ # we set it to null to signify that. Technically, this
+ # is bad data model design -- we should just get rid of
+ # ongoing_p
+ if {[string equal $ongoing_p t]} {
+ set actual_end_date ""
+ set planned_end_date ""
+ }
+
+ # create a logger project
+ set logger_project [logger::project::new \
+ -name $project_name \
+ -description $description \
+ -project_lead $creation_user \
+ ]
+
+ # create a project manager project (associating the logger project
+ # with the logger project)
+ set project_revision [db_exec_plsql new_project_item { *SQL }]
+
+ # add in the default variable (hopefully hours)
+ logger::project::map_variable \
+ -project_id $logger_project \
+ -variable_id [logger::variable::get_default_variable_id]
+
+ return $project_revision
}
-ad_proc -public project_manager::project::latest_start {
- end_date_j
- hours_to_complete
- hours_day
+
+ad_proc -public pm::project::delete {
+ -project_item_id:required
} {
+ Stub for project deletion
+
+ @author Jade Rubick (jader@bread.com)
+ @creation-date 2004-03-03
+
+ @param project_item_id
- # set latest_start($my_iid) [expr $task_deadline_j - [expr $activity_time($my_iid) / double($hours_day)]]
+ @return
+
+ @error
+} {
+
+ # should we delete the logger project as well?
- # we now set the latest start. This is equal to the
- # latest finish date minus the amount of time it will
- # take to accomplish the job. We need to disregard holidays!
+}
+
+ad_proc -public pm::project::edit {
+ -project_item_id:required
+ -project_name:required
+ {-project_code ""}
+ {-parent_id ""}
+ {-goal ""}
+ {-description ""}
+ {-planned_start_date ""}
+ {-planned_end_date ""}
+ {-actual_start_date ""}
+ {-actual_end_date ""}
+ -logger_project:required
+ {-ongoing_p "f"}
+ -status_id:required
+ -organization_id:required
+ {-creation_date ""}
+ -creation_user:required
+ -creation_ip:required
+ -package_id:required
+} {
+ Stub for project edit
+
+
PROJECTS: estimated_completion_date timestamptz, earliest_completion_date timestamptz, @@ -254,22 +550,45 @@ earliest_finish(i) = earliest_start(i) + activity_time(i) latest_start(i) = min(last_start(i+1) - activity_time(i) latest_finish(i) = latest_start(i) + activity_time(i) ++ Tasks in ongoing projects are given null completion dates. + + + The statistics are computed based on: + - project statistics are based on that project + subproject statistics + Project statistics are based on that project + subproject statistics + - that means if a project has a subproject, then the tasks for both of those projects are put together in one list, and computed together. - - so for a project with no subprojects, the values are computed for the tasks in that project + That means if a project has a subproject, then the tasks for + both of those projects are put together in one list, and computed + together. - for a project with subprojects, the statistics are based on the tasks of both of those projects. + - this function returns a list of task_item_ids of all tasks under - a project, plus all subproject tasks. + So for a project with no subprojects, the values are computed + for the tasks in that project + + For a project with subprojects, the statistics are based on the + tasks of both of those projects. + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-02-19 + + @param project_item_id The item_id for the project + + @return a list of task_item_ids of all tasks under a project, plus all subproject tasks. This is done so that the function can be recursive + + @error No error codes + } { + # Before hacking on this, you might want to look at: + # http://www.joelonsoftware.com/articles/fog0000000069.html + # TODO: # # ------------------------------------------------------------------------- @@ -288,7 +607,7 @@ # should look at: # http://mscmga.ms.ic.ac.uk/jeb/or/netaon.html - ns_log Notice "-----------------------------------------" + # ns_log Notice "-----------------------------------------" # -------------------------------------------------------------------- # for now, hardcode in a day is 8 hours. Later, we want to set this by @@ -312,7 +631,7 @@ # --------------------------------------------- # gets all tasks that are a part of subprojects # --------------------------------------------- - set project_return [project_manager::project::compute_status $my_id] + set project_return [pm::project::compute_status $my_id] set task_list_project [concat $task_list_project $project_return] } elseif {[string equal $my_type "pm_task"]} { @@ -322,7 +641,7 @@ set task_list [concat $task_list $task_list_project] - ns_log Notice "task_list: $task_list" + # ns_log Notice "task_list: $task_list" # ------------------------- # no tasks for this project @@ -339,7 +658,7 @@ # today_j (julian date for today) db_1row tasks_group_query { } - ns_log notice "Julian today: $today_j" + # ns_log notice "Julian today: $today_j" # -------------------------------------------------------------- # Set up activity_time for all tasks @@ -352,7 +671,7 @@ if {[exists_and_not_null task_deadline_j]} { - ns_log notice "$my_iid has a deadline $task_jdeadline_j" + # ns_log notice "$my_iid has a deadline $task_jdeadline_j" set latest_finish($my_iid) $task_deadline_j @@ -376,7 +695,7 @@ set dependency_types($task_item_id-$parent_task_id) $dependency_type - ns_log Notice "dependency (id: $dependency_id) task: $task_item_id parent: $parent_task_id type: $dependency_type" + # ns_log Notice "dependency (id: $dependency_id) task: $task_item_id parent: $parent_task_id type: $dependency_type" } @@ -385,9 +704,16 @@ # task information off of them # -------------------------------------------------------------- - # gives up planned_start_date and planned_end_date + # gives up end_date_j, start_date_j, and ongoing_p + # if ongoing_p is t, then end_date_j should be null db_1row project_info { } + if {[string equal $ongoing_p t] && ![empty_string_p end_date_j]} { + ns_log Error "Project cannot be ongoing and have a non-null end-date" + set end_date_j "" + } + + # -------------------------------------------------------------- # task_list contains all the tasks # a subset of those do not depend on any other tasks @@ -426,19 +752,19 @@ lappend present_tasks $task_item - ns_log Notice "Begin earliest_start($task_item): $earliest_start($task_item)" + # ns_log Notice "Begin earliest_start($task_item): $earliest_start($task_item)" } } # ------------------------------- # stop if we have no dependencies # ------------------------------- if {[llength $present_tasks] == 0} { - ns_log Notice "No tasks with dependencies" + # ns_log Notice "No tasks with dependencies" return [list] } - ns_log Notice "present_tasks: $present_tasks" + # ns_log Notice "present_tasks: $present_tasks" # ------------------------------------------------------ # figure out the earliest start and finish times @@ -450,7 +776,7 @@ foreach task_item $present_tasks { - ns_log Notice "this task_item: $task_item" + # ns_log Notice "this task_item: $task_item" # ----------------------------------------------------- # some tasks may already have earliest_start filled in @@ -460,7 +786,7 @@ if {![exists_and_not_null earliest_start($task_item)]} { - ns_log Notice " !info exists for $task_item" + # ns_log Notice " !info exists for $task_item" # --------------------------------------------- # set the earliest_start for this task = @@ -484,10 +810,10 @@ # set earliest_finish($task_item) [expr $max_earliest_start + [expr $activity_time($task_item) / double($hours_day)]] set earliest_finish($task_item) [earliest_finish $max_earliest_start $activity_time($task_item) $hours_day] - ns_log Notice \ - " earliest_start ($task_item): $earliest_start($task_item)" - ns_log Notice \ - " earliest_finish($task_item): $earliest_finish($task_item)" + # ns_log Notice \ + # " earliest_start ($task_item): $earliest_start($task_item)" + # ns_log Notice \ + # " earliest_finish($task_item): $earliest_finish($task_item)" } @@ -500,7 +826,7 @@ } } - ns_log Notice "future tasks: $future_tasks" + # ns_log Notice "future tasks: $future_tasks" set present_tasks $future_tasks } @@ -513,7 +839,7 @@ foreach task_item $task_list { - ns_log Notice "*Earliest start ($task_item): $earliest_start($task_item)" + # ns_log Notice "*Earliest start ($task_item): $earliest_start($task_item)" if {$max_earliest_finish < $earliest_finish($task_item)} { set max_earliest_finish $earliest_finish($task_item) } @@ -523,8 +849,16 @@ # ----------------------------------------------------------------- # Now compute latest_start and latest_finish dates. # Note the latest_finish dates may be set to an arbitrary deadline. + # Also note that it is possible for a project to be ongoing. + # In that case, the latest_start and latest_finish dates should + # be set to null. # ----------------------------------------------------------------- - + # If these represent the dependency hierarchy: + # 2155 + # / | \ + # 2161 2173 2179 + # | | + # 2167 2195 # ---------------------------------------------------------------------- # we want to go through and fill in all the values for latest start # and latest_finish. @@ -538,13 +872,42 @@ # add latest_finish values for. We call these lists # present_tasks and future_tasks # ---------------------------------------------------------------------- + # Here's a description of the algorithm. + # 1. The algorithm starts with those tasks that don't have other + # tasks depending on them. + # + # So in the example above, we'll start with + # present_tasks: 2167 2173 2195 + # future tasks: + # + # 2. While we make the present_tasks list, we store latest_start + # and latest_finish information for each of those tasks. If the + # project is ongoing, then we also keep track of tasks that have + # no latest_start or latest_finish. We keep this in the + # ongoing_task(task_id) array. If is exists, then we know that + # that task is an ongoing task, so no deadline will exist for it. + # + # 3. Stop if we don't have any dependencies + # + # 4. Then we get into a loop. + # While there are present_tasks: + # Create the future_tasks list + # For each present task: + # If the task has a dependent task: + # Go through these dependent tasks: + # If the dependent task is ongoing don't defer + # If the dependent task doesn't have LS set, + # then defer, and add to future_tasks list + # Otherwise set the LS value for that task + # If there are no deferals, get the minimum LS of + # dependents, set LF + # Add the dependent tasks to the future_tasks + # Set present_tasks equal to future_tasks, clear future_tasks + + # ---------------------------------------------------------------------- # The biggest problem with this algorithm is that you can have items at - # two different levels in the hierarchy. For example, - # 2155 - # / | \ - # 2161 2173 2179 - # | | - # 2167 2195 + # two different levels in the hierarchy. + # # if you trace through this algorithm, you'll see that we'll get to 2155 # before 2161's values have been set, which can cause an error. The # solution we arrive at is to defer to the future_tasks list any item @@ -560,41 +923,67 @@ # info for these items # ----------------------------------------------------- - ns_log Notice "Starting foreach task-item $task_list" + # ns_log Notice "Starting foreach task-item $task_list" foreach task_item $task_list { if {![info exists dependent($task_item)]} { - ns_log Notice " !info exists dependent($task_item)" + # ns_log Notice " !info exists dependent($task_item)" # we check this because some tasks already have # hard deadlines set. if {[info exists latest_finish($task_item)]} { - if {$end_date_j < $latest_finish($task_item)} { - set latest_finish($task_item) $end_date_j + # if the project needs to be completed before the + # actual hard deadline, then the project deadline + # has precedence. However, sometimes the project is + # ongoing, so we have to make sure that there actually + # is an end_date_j + if {![empty_string_p end_date_j]} { + if {$end_date_j < $latest_finish($task_item)} { + set latest_finish($task_item) $end_date_j + } } - #set late_start_temp [expr $latest_finish($task_item) - [expr $activity_time($task_item) / double($hours_day)]] - set late_start_temp [latest_start $latest_finish($task_item) $activity_time($task_item) $hours_day] + # we also set the latest_start date + set late_start_temp \ + [latest_start \ + -end_date_j $latest_finish($task_item) \ + -hours_to_complete $activity_time($task_item) \ + -hours_day $hours_day] + if {$late_start_temp < $latest_start($task_item)} { set latest_start($task_item) $late_start_temp } } else { - set latest_finish($task_item) $end_date_j - #set latest_start($task_item) [expr $latest_finish($task_item) - [expr $activity_time($task_item) / double($hours_day)]] - set latest_start($task_item) [latest_start $latest_finish($task_item) $activity_time($task_item) $hours_day] - } + # this section is for items that have no solid + # deadline, but also have no items dependent on them + # we either set the latest start and finish of the item or + # we specify that the task is an ongoing task + if {[empty_string_p $end_date_j]} { + set ongoing_task($task_item) true + # ns_log Notice "NSDBAHNITD: end_date_j was empty ti:$task_item" + } else { + set latest_finish($task_item) $end_date_j + + set latest_start($task_item) \ + [latest_start \ + -end_date_j $latest_finish($task_item) \ + -hours_to_complete $activity_time($task_item) \ + -hours_day $hours_day] + + } + } lappend present_tasks $task_item - ns_log Notice "Begin latest_start($task_item): $latest_start($task_item) latest_finish: $latest_finish($task_item)" + # ns_log Notice "Begin latest_start($task_item): $latest_start($task_item) latest_finish: $latest_finish($task_item)" } else { - ns_log Notice " info exists dependent($task_item)" + #ns_log Notice " info exists dependent($task_item)" } } @@ -603,11 +992,11 @@ # stop if we have no dependencies # ------------------------------- if {[llength $present_tasks] == 0} { - ns_log Notice "No tasks with dependencies" + # ns_log Notice "No tasks with dependencies" return [list] } - ns_log Notice "LATEST present_tasks: $present_tasks" + # ns_log Notice "LATEST present_tasks: $present_tasks" # ------------------------------------------------------ # figure out the latest start and finish times @@ -619,7 +1008,7 @@ foreach task_item $present_tasks { - ns_log Notice "this task_item: $task_item" + # ns_log Notice "this task_item: $task_item" # ----------------------------------------------------- # some tasks may already have latest_start filled in. @@ -629,74 +1018,135 @@ if {[info exists dependent($task_item)]} { - ns_log Notice " info exists for dependent($task_item)" + # ns_log Notice " info exists for dependent($task_item)" # --------------------------------------------- # set the latest_start for this task = - # min(latest_start(i+1) - activity_time(i) + # min(latest_start(i+1) - activity_time(i)) # # (i+1 means an item that depends on this task) # (i means this task) # --------------------------------------------- + # we set this to the end date, and then move it back + # as we find dependent items that have earlier + # latest_start dates. The problem is that the + # end_date_j is empty when there is no deadline. + # So we need to remember that min_latest_start can + # be an empty value set min_latest_start $end_date_j - + foreach dependent_item $dependent($task_item) { + + if {[exists_and_not_null ongoing_task($dependent_item)]} { + set defer_p f + set my_latest_start "" + # ns_log Notice "ongoing_task, no defer" + + } elseif {![exists_and_not_null latest_start($dependent_item)]} { + # we defer the task if the dependent item has no + # latest_start date set - if {![exists_and_not_null latest_start($dependent_item)]} { - # let's not do this task_item yet - lappend future_tasks $task_item - ns_log Notice " defer" - set defer_p t - } else { + if {[info exists defer_count($task_item)]} { + incr defer_count($task_item) + } else { + set defer_count($task_item) 1 + } - #set my_latest_start [expr $latest_start($dependent_item) - [expr $activity_time($dependent_item) / double($hours_day)]] + # we use a magic number here. + # basically, we don't want to defer the + # item forever. Ideally, this should + # be cleaned up better. Defering is necessary + # given this algorithm, but there are + # times when you don't want to defer. + # This is hackish, and I'm embarrased, but on + # a deadline. :( + if {$defer_count($task_item) > 5} { + set defer_p f + # ns_log Notice " no defer because defer count exceeded" + } else { + lappend future_tasks $task_item + # ns_log Notice " defer" + set defer_p t + } + - set my_latest_start [latest_start $latest_start($dependent_item) $activity_time($task_item) $hours_day] - ns_log Notice " my_latest_start: $my_latest_start" + } else { + + # the dependent item has a deadline + + set my_latest_start \ + [latest_start \ + -end_date_j $latest_start($dependent_item) \ + -hours_to_complete $activity_time($task_item) \ + -hours_day $hours_day] + + # ns_log Notice " my_latest_start: $my_latest_start" + if {$my_latest_start < $min_latest_start} { set min_latest_start $my_latest_start } - + set defer_p f } + } - + if {[string equal $defer_p f]} { - + # we check that latest_start doesn't already exist # which it might for hard-deadlines if {[exists_and_not_null latest_start($task_item)]} { if {$min_latest_start < $latest_start($task_item)} { set latest_start($task_item) $min_latest_start } } else { + + # so this task has no hard deadline. + # We now set the value to the minimum of the + # dependent tasks. Note that if the dependent + # tasks all have no hard deadlines, and the + # project is ongoing, then the value will be + # set to "" + set latest_start($task_item) $min_latest_start } - ns_log Notice " min_latest_start: $min_latest_start" + # ns_log Notice " min_latest_start: $min_latest_start" - # temp is temporary latest_finish - #set temp [expr $min_latest_start + [expr $activity_time($task_item) / double($hours_day)]] - set temp [my_latest_finish $min_latest_start $activity_time($task_item) $hours_day] - - if {[exists_and_not_null latest_finish($task_item)]} { - if {$temp < $latest_finish($task_item)} { - set latest_finish($task_item) - } + # we now set the latest finish. Ongoing tasks set + # the latest finish to empty + if {[empty_string_p $latest_start($task_item)]} { + set temp_lf "" } else { - set latest_finish($task_item) $temp + set temp_lf [my_latest_finish $min_latest_start $activity_time($task_item) $hours_day] } - ns_log Notice \ - " latest_start ($task_item): $latest_start($task_item)" - ns_log Notice \ - " latest_finish($task_item): $latest_finish($task_item)" + # if there is already a hard deadline for this + # task, then we check whether temp_lf is earlier, + # and set it to temp_lf if so + + if {[empty_string_p $temp_lf]} { + set latest_finish($task_item) "" + } else { + if {[exists_and_not_null latest_finish($task_item)]} { + if {$temp_lf < $latest_finish($task_item)} { + set latest_finish($task_item) $temp_lf + } + } else { + set latest_finish($task_item) $temp_lf + } + } + + #ns_log Notice \ + # " latest_start ($task_item): $latest_start($task_item)" + #ns_log Notice \ + # " latest_finish($task_item): $latest_finish($task_item)" } else { - ns_log Notice "Deferring $task_item" + # ns_log Notice "Deferring $task_item" } - } + } # ------------------------------- # add to list of tasks to process @@ -707,7 +1157,7 @@ } } - ns_log Notice "future tasks: $future_tasks" + # ns_log Notice "future tasks: $future_tasks" set present_tasks $future_tasks } @@ -716,14 +1166,22 @@ # set up latest start date for project # ---------------------------------------------- - set min_latest_start $end_date_j + if {[empty_string_p $end_date_j]} { + set min_latest_start "" + set max_earliest_finish "" + } else { + set min_latest_start $end_date_j + + foreach task_item $task_list { - foreach task_item $task_list { - - ns_log Notice "*Latest start ($task_item): $latest_start($task_item)" - if {$min_latest_start > $latest_start($task_item)} { - set max_earliest_finish $earliest_finish($task_item) + # ns_log Notice "*Latest start ($task_item): $latest_start($task_item)" + if {$min_latest_start > $latest_start($task_item)} { + set max_earliest_finish $earliest_finish($task_item) + } } + + set max_earliest_finish "J[expr floor([set max_earliest_finish])]" + set min_latest_start "J[expr floor([set min_latest_start])]" } @@ -736,11 +1194,26 @@ # this is very inefficient and stupid foreach task_item $task_list { + set es "J[expr ceil( [set earliest_start($task_item)])]" + set ef "J[expr ceil( [set earliest_finish($task_item)])]" + + if {[exists_and_not_null latest_start($task_item)]} { + set ls "J[expr floor([set latest_start($task_item)])]" + } else { + set ls "" + } + + if {[exists_and_not_null latest_finish($task_item)]} { + set lf "J[expr floor($latest_finish($task_item))]" + } else { + set lf "" + } + db_dml update_task { } } - ns_log Notice "*******************" + # ns_log Notice "*******************" return $task_list @@ -749,12 +1222,12 @@ -ad_proc -public project_manager::project::compute_parent_status {project_item_id} { +ad_proc -public pm::project::compute_parent_status {project_item_id} { When a project is updated, or a task updated within a project, we need to update all the projects higher in the hierarchy. } { - ns_log Notice "computing parents for $project_item_id" + # ns_log Notice "computing parents for $project_item_id" set package_id [ad_conn package_id] @@ -772,10 +1245,36 @@ set my_item_id $parent_id } - ns_log Notice "root: $root_folder , last_item_id $last_item_id" + # ns_log Notice "root: $root_folder , last_item_id $last_item_id" set return_code [compute_status $last_item_id] return $return_code } + + +ad_proc -public pm::project::get_logger_project { + -project_item_id:required +} { + Returns logger's project ID when given project manager's project ID + + @author Jade Rubick (jader@bread.com) + @creation-date 2004-03-04 + + @param project_item_id + + @return logger's project_id + + @error returns no_project if no such project_item_id exists +} { + return [db_string get_logger_project " + SELECT + logger_project + FROM + pm_projects + WHERE + project_id = + (select live_revision from cr_items where item_id = :project_item_id) + " -default "no_project"] +}