Link to home
Start Free TrialLog in
Avatar of t3chguy
t3chguyFlag for United States of America

asked on

Generating Future dates based on condition

I am working on a project management system and I am trying to get an estimated start date based on the previous projects length and duration.  I am putting a "dummy" project in the system with a completion day to help base the future dates off of.

I want each project to look at the previous projects completion date.  If that is not set, then it should look at the dates table and see if there are any dates that have been added and add the task length's accordingly.

The dates that are held in the dates table are the start date (which is set by the programmer who will press a start button to begin a project), a pause date (which can be set by the programmer if the project is going on a temporary hold), a resume date, (which will be again set by the programmer when the paused project will be resumed).

The code below is basing all projects based off of the completion date, and is not looking at the previous project to calculate dates.  
 
foreach($programmers AS $programmer)
	{
	//Set End Date for last completed project!	
	$lastCompletedProject = lastCompletedProject($programmer);
	
	echo 'Last Completed Project: ' . $lastCompletedProject . '<br />';
	
	//Get Projects
	$getProjects = mysql_query("SELECT helpDesk_projectList.id, createdFor, projectName, projectPriority, projectStatus, programmerAssigned
													FROM helpDesk_projectList
													LEFT JOIN helpDesk_projectAssignments ON helpDesk_projectList.id = helpDesk_projectAssignments.projectId
													WHERE (projectStatus = '1' OR projectStatus = '2' OR projectStatus = '3') and programmerAssigned = '".$programmer."'") or die("Cannot get current projects: " . mysql_error());
						
	if(mysql_num_rows($getProjects) > 0)
		{
		$i = 0;
		$prevRow = 0;
		
		while($pData = mysql_fetch_assoc($getProjects))
			{
			//Sum Days of Tasks for each project
			$getTasks = mysql_query("SELECT SUM(taskLength) as taskLength FROM helpDesk_projectTasks WHERE projectId = '".$pData['id']."'") or die("Cannot get Tasks: " . mysql_error());

			if(mysql_num_rows($getTasks) > 0)
				{
				while($taskData = mysql_fetch_assoc($getTasks))
					{
					$taskLength = $taskData['taskLength'];
					}
				}
				
			//Temp BS
			$deadlineDate = date("Y-m-d", strtotime($projectLengthMinusOne . ' weekdays', strtotime($startDate)));
				
			//Check for any other projects that may have dates associated with them.
			$checkProjects = mysql_query("SELECT actionDate, actionType FROM helpDesk_projectDates WHERE projectId = '".$pData['id']."' AND actionType <> '1'") or die("Cannot check projects: " . mysql_error());
			
			if(mysql_num_rows($checkProjects) > 0)
				{
				while($cpData = mysql_fetch_assoc($checkProjects))
					{
					//add dates to the last action date.
					$lastActionDate = $cpData['actionDate'];
					$nextStart = 'duh' . date("Y-m-d", strtotime($taskLength . ' weekdays', strtotime($lastActionDate)));
					}
				}
				
			else
				{
				if($prevRow == $currentRow)
					{
					//Last completed + 1
					$nextDay =  date("Y-m-d", strtotime('+1 weekday', strtotime($lastCompletedProject)));
					}
					
				else
					{
					$nextDay = 'Not sure??';
					}
					
				//Add dates to last completed date.
				$endDate = 'lol ' . date("Y-m-d", strtotime($taskLength . ' weekdays', strtotime($nextDay)));
				}
					
		
		//Add Task Length to prevoius project Completion Date
			
		echo '<tr>
					<td ' . $row_color . '>'.$pData['id'] . '</td>
					<td ' . $row_color . '>'.$pData['projectName'] . '</td>
					<td ' . $row_color . '>'.getCkname($pData['programmerAssigned']) . '</td>
					<td ' . $row_color . '>'.$pData['projectPriority'] . '</td>
					<td ' . $row_color . '>'.$estimatedStart . '</td>
					<td ' . $row_color . '>'.$estimatedEnd . '</td>
					<td ' . $row_color . '>'.$taskLength .  '</td>
					<td ' . $row_color . '>'.$nextDay . '</td>
					<td ' . $row_color . '>'.$endDate . '</td>
					<td ' . $row_color . '><span class="boldblacklinks"><a href="editProject.php?projectId='.$projectId.'" class="iframetall">Edit</a></span></td>
				</tr>';
			
			$prevRow = $currentRow;
			}
		}
	}

Open in new window

Screenshot.png
helpDesk-projectAssignments.sql
helpDesk-projectDates.sql
helpDesk-projectList.sql
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

What is the question?  Do you want to know how to do date arithmetic?  The DateTime extension is helpful.  This article may get you started.
https://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/A_201-Handling-date-and-time-in-PHP-and-MySQL.html
Avatar of t3chguy

ASKER

Not so much date arithmetic, but how can I use the last date generated to calculate future dates?

For example, in my screenshot, The Dashboard Updates project starts on 10-15 and has a duration of two days.  That would mean that project Test 2 should start on 10-18 and last for three days for an end date of 10-21.  Right now, its basing all start and end dates based off of the same date which is 10-14.

The question is about iterating dates based on the previous row's values.
That sounds like date arithmetic to me.  I'll see if I can give you an example.  It won't use a data base, but the step from rows in a query results set to the code I show will not be very big at all.
Please see: http://www.laprbass.com/RAY_temp_t3chguy.php

<?php // RAY_temp_t3chguy.php 2013-10-16
error_reporting(E_ALL);
echo '<pre>';

// SCHEDULE BASED ON CONSECUTIVE TASKS
// http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_28268506.html#a39576941

// USE THIS TO SIMULATE A SET OF TASK DURATIONS
$tasks = array
( 'one' => 2
, 'two' => 3
, 'tre' => 4
, 'for' => 1
, 'fiv' => 2
)
;

// WE WILL CAPTURE THE TASK START AND END DAYS HERE
$out = array();

// OUR STARTING DAY WILL BE TOMORROW
$alpha = date('Y-m-d', strtotime('Tomorrow'));

// ADD IN THE TASKS
foreach ($tasks as $item => $num)
{
    // THE NUMBER OF WHOLE DAYS
    $omega = date('Y-m-d', strtotime($alpha . " + $num DAYS - 1 SECOND"));

    // SAVE THE RESULTS
    $ndx = $item . "($num days)";
    $out[$ndx] = array("Start" => $alpha, "Ended" => $omega);

    // START THE NEXT TASK ON THE DAY FOLLOWING THE PRIOR COMPLETION
    $alpha = date('Y-m-d', strtotime($omega . " + 1 DAY"));
}

print_r($out);

Open in new window

At some point you may also want to consider weekends and holidays as gaps in the schedule.  See practical application #9 in this article.
https://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/A_201-Handling-date-and-time-in-PHP-and-MySQL.html
Avatar of t3chguy

ASKER

That part I understand, but do not understand how to apply that to a while loop for my specific use.
Are you wanting to update the data base table with start / end dates?  Or are you looking to insert new rows?
Avatar of t3chguy

ASKER

Neither, hoping just to do this dynamically in the code.
Looking at this...

$nextStart = 'duh' . date("Y-m-d", strtotime($taskLength . ' weekdays', strtotime($lastActionDate)));

Open in new window

Not sure what effect the 'duh' is having on the process, but if it's getting carried forward into a future computation, it won't be helpful.

Any chance you want something like this?

// MAKE ISO-8601 OF THE LAST DATE + 1 
$lastActionDate = date('Y-m-d', strtotime($lastActionDate));

// ADD WEEK DAYS
$nextStart      = date('Y-m-d', strtotime($lastActionDate . " + $taskLength WEEKDAYS"));

Open in new window

If that's not it, please show me an isolated example of the while() loop with a small test set in an array.
Avatar of t3chguy

ASKER

Programmer 1 had a last completion date of 2013-10-14

Project ID 3 should start on 10-15 and have a duration of 2 days ending on 10/16.
Project ID 5 should start on 10-17 and have a duration of 3 days ending on 10/20.


	$getProjects = mysql_query("SELECT helpDesk_projectList.id, createdFor, projectName, projectPriority, projectStatus, programmerAssigned
													FROM helpDesk_projectList
													LEFT JOIN helpDesk_projectAssignments ON helpDesk_projectList.id = helpDesk_projectAssignments.projectId
													WHERE (projectStatus = '1' OR projectStatus = '2' OR projectStatus = '3') and programmerAssigned = '".$programmer."'") or die("Cannot get current projects: " . mysql_error());
	
	if(mysql_num_rows($getProjects) > 0)
		{
		$i = 0;
		$prevRow = 0;
		
		while($pData = mysql_fetch_assoc($getProjects))
			{
			//Sum Days of Tasks for each project
			$getTasks = mysql_query("SELECT SUM(taskLength) as taskLength FROM helpDesk_projectTasks WHERE projectId = '".$pData['id']."'") or die("Cannot get Tasks: " . mysql_error());

			if(mysql_num_rows($getTasks) > 0)
				{
				while($taskData = mysql_fetch_assoc($getTasks))
					{
					$taskLength = $taskData['taskLength'];
					}
				}
						
			//Check for any other projects that may have dates associated with them.
			$checkProjects = mysql_query("SELECT actionDate, actionType FROM helpDesk_projectDates WHERE projectId = '".$pData['id']."' AND actionType <> '1'") or die("Cannot check projects: " . mysql_error());
			
			if(mysql_num_rows($checkProjects) > 0)
				{
				while($cpData = mysql_fetch_assoc($checkProjects))
					{
					//add dates to the last action date.
					$lastActionDate = $cpData['actionDate'];
					$nextStart = date("Y-m-d", strtotime($taskLength . ' weekdays', strtotime($lastActionDate)));
					}
				}
				
			else
				{
				//Last completed + 1
				$nextDay =  date("Y-m-d", strtotime('+1 weekday', strtotime($lastCompletedProject)));
				}
			
				//Add dates to last completed date.
				$endDate = date('Y-m-d', strtotime($alpha . " + $taskLength DAYS - 1 SECOND"));
				
					
				// MAKE ISO-8601 OF THE LAST DATE + 1 
				$lastActionDate = date('Y-m-d', strtotime($nextDay));

				// ADD WEEK DAYS
				$nextEnd      = date('Y-m-d', strtotime($lastActionDate . " + $taskLength WEEKDAYS"));

				//Add Task Length to prevoius project Completion Date
			
				echo '<tr>
							<td ' . $row_color . '>'.$pData['id'] . '</td>
							<td ' . $row_color . '>'.$pData['projectName'] . '</td>
							<td ' . $row_color . '>'.getCkname($pData['programmerAssigned']) . '</td>
							<td ' . $row_color . '>'.$lastActionDate . '</td>
							<td ' . $row_color . '>'.$nextEnd . '</td>
							<td ' . $row_color . '><span class="boldblacklinks"><a href="editProject.php?projectId='.$projectId.'" class="iframetall">Edit</a></span></td>
						</tr>';
			
			$prevRow = $currentRow;
			}
		}
	}

Open in new window

Please add error_reporting(-1) to the top of this script so we can be sure that it's not relying on any undefined variables.

This is line 34:

$nextStart = date("Y-m-d", strtotime($taskLength . ' weekdays', strtotime($lastActionDate)));

Open in new window

What is the value that the script creates in $nextStart?  Please print it out with var_dump() so we can see what is begin created there.
Avatar of t3chguy

ASKER

$nextStart returns

NULL

NULL

NULL 

Open in new window

That's kind of what I expected.  It looks like the intent of that line of code is to add some number of weekdays (identified in the variable $taskLength) to a date identified in the variable $lastActionDate.  If that's a fair explanation, please try that line like this and let's see what var_dump() can tell us after the code change.

$nextStart = date("Y-m-d", strtotime($lastActionDate . " + $taskLength weekdays"));

Open in new window

Please correct any typos - I cannot test any of this, so I'm coding without a net.
Avatar of t3chguy

ASKER

It still returns NULL.  Even though the code above it, ($lastActionDate) is not empty.
There is something else going wrong here.  Neither of the methods of computing the future date return NULL.

<?php // RAY_temp_t3chguy_1.php 2013-10-16
error_reporting(E_ALL);
echo '<pre>';

// SCHEDULE BASED ON CONSECUTIVE TASKS
// http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_28268506.html#a39576941

$taskLength = 4;
$lastActionDate = '2013-10-16';
$nextStart = date("Y-m-d", strtotime($taskLength . ' weekdays', strtotime($lastActionDate)));
var_dump($nextStart);

$nextStart = date("Y-m-d", strtotime($lastActionDate . " + $taskLength weekdays"));
var_dump($nextStart);

Open in new window

I'm beginning to think there is a program logic error that is skipping some or all of the code.  It may need to be rewritten to find the error.  Here's an example of the sort of code that should be refactored, and an example of how to run queries and show the errors, if any.

<?php // RAY_temp_t3chguy_3.php 2013-10-16
error_reporting(E_ALL);
echo '<pre>';

// SCHEDULE BASED ON CONSECUTIVE TASKS
// http://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/Q_28268506.html#a39576941


// WHAT WE HAVE
$getTasks = mysql_query("SELECT SUM(taskLength) as taskLength FROM helpDesk_projectTasks WHERE projectId = '".$pData['id']."'") or die("Cannot get Tasks: " . mysql_error());
if(mysql_num_rows($getTasks) > 0)


// THIS IS MORE LIKE WHAT WE NEED
$getTasksQry = "SELECT SUM(taskLength) as taskLength FROM helpDesk_projectTasks WHERE projectId = '{$pData["id"]}' ";
$getTasks    = mysql_query($getTasksQry);
if (!$getTasks)
{
    $err = "<br>FAIL: $getTasksQry";
    $err .= "<br>ERRNO: " . mysql_errno();
    $err .= "<br>ERROR: " . mysql_error();
    trigger_error($err, E_USER_ERROR);
}
$numTasks = mysql_num_rows($getTasks);
if ($numTasks)
{
    echo "<br>getTasksQry FOUND $numTasks ROWS";
}
else
{
    echo "<br>getTasksQry FOUND NO ROWS";
}

Open in new window

This article shows how to use PHP and MySQL together.  You're going to have to convert all of the procedural MySQL to MySQLi or PDO anyway, might as well start with good examples of how to do it.
https://www.experts-exchange.com/Web_Development/Web_Languages-Standards/PHP/PHP_Databases/A_11177-PHP-MySQL-Deprecated-as-of-PHP-5-5-0.html
Avatar of t3chguy

ASKER

Ok, I moved your code into my pre-existing block.

There are three projects in the system.

Running your code returned the following:

getTasksQry FOUND 1 ROWS
getTasksQry FOUND 1 ROWS
getTasksQry FOUND 1 ROWS
Great - we're moving in the right direction.

Do you expect the query to find 1 row every time?  If so, you might want to add LIMIT 1 to the query so the SQL engine doesn't do a table scan every time (see AntiPHPractice #26).

Next, please use print_r() to print out the rows that were found so we can see what data is in there.
Avatar of t3chguy

ASKER

Yes, it should typically always always return one row.  I will add that limit to the query.

getTasksQry FOUND 1 ROWSArray
(
    [taskLength] => 2
)

getTasksQry FOUND 1 ROWSArray
(
    [taskLength] => 3
)



getTasksQry FOUND 1 ROWSArray
(
    [taskLength] => 63
)

Open in new window

Good.   We know that taskLength makes sense.  Next step is to repeat this pattern of data discovery on the other data elements.  Run the queries, print them out and make sure every one of them is showing what you expect.
Avatar of t3chguy

ASKER

getTasksQry FOUND 1 ROWS Last Completed Project: 2013-10-14
Project Id: 3 -  2 days - 2013-10-15 - 2013-10-17

getTasksQry FOUND 1 ROWS Last Completed Project: 2013-10-14
Project Id: 5 -  3 days - 2013-10-15 - 2013-10-18

Open in new window


Everything is looking good.  Here is the part that needs to be changed somehow.

Project ID 3 is displaying exactly how it should.

HOWEVER, Project ID # 5 is to be completed AFTER project Id #3, but there is no logic in the code stating that these projects do not run at the same time, but instead, one after another.  

The logic to make that happen is what I'm unsure of.
Yes, I'm unsure of it, too.  Can you please post the script as it is now?  I'll try to follow the variables.  In a semi-related matter, you might want to get the assistance of a data base administrator.  It might be worth rethinking the tables structure.
Avatar of t3chguy

ASKER

foreach($programmers AS $programmer)
	{
	echo '<strong>' . getCkname($programmer) . ' Projects</strong><br /><br />';
	
	//Get Projects
	$getProjects = mysql_query("SELECT helpDesk_projectList.id, createdFor, projectName, projectPriority, projectStatus, programmerAssigned
													FROM helpDesk_projectList
													LEFT JOIN helpDesk_projectAssignments ON helpDesk_projectList.id = helpDesk_projectAssignments.projectId
													WHERE (projectStatus = '1' OR projectStatus = '2' OR projectStatus = '3') and programmerAssigned = '".$programmer."'") or die("Cannot get current projects: " . mysql_error());
	
	if(mysql_num_rows($getProjects) > 0)
		{
		$z = 1;
		
		while($pData = mysql_fetch_assoc($getProjects))
			{
			$getTasksQry = "SELECT SUM(taskLength) as taskLength FROM helpDesk_projectTasks WHERE projectId = '{$pData["id"]}' LIMIT 1";
			$getTasks    = mysql_query($getTasksQry);
			if (!$getTasks)
				{
				$err = "<br>FAIL: $getTasksQry";
				$err .= "<br>ERRNO: " . mysql_errno();
				$err .= "<br>ERROR: " . mysql_error();
				trigger_error($err, E_USER_ERROR);
				}

			$numTasks = mysql_num_rows($getTasks);

			while($tData = mysql_fetch_assoc($getTasks))
				{
				$taskLength = $tData['taskLength'];
				}
				
			$currentProject = $pData['id'];

			if($z == '1')
				{
				//Set End Date for last completed project!	
				$lastCompletedProject = lastCompletedProject($programmer);
				$nextStart = date("Y-m-d", strtotime($lastCompletedProject . " + 1 weekday"));
				$estimatedEnd = date("Y-m-d", strtotime($nextStart . " + $taskLength weekdays"));
				}
				
			if($z > '1')
				{
				$lastCompletedProject = $estimatedEnd;
				$nextStart = date("Y-m-d", strtotime($lastCompletedProject . " + 1 weekday"));
				$estimatedEnd = date("Y-m-d", strtotime($nextStart . " + $taskLength weekdays"));
				$estimatedEnd = date("Y-m-d", strtotime($estimatedEnd . " 1 weekday"));
				}
		
			echo 'Last Completed Project: ' . $lastCompletedProject . '<br /><br />';

			//Print Task Length and Start & End Dates.
			echo '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Project Id: ' . $pData['id'] . ' - &nbsp;' . $taskLength . ' days - ' . $nextStart . ' - ' . $estimatedEnd . '<br />'; 
			
			$z++;
			
			echo '<br />';
			}
		}

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of t3chguy

ASKER

That certainly helps put me on the right track.  Thank you.  The problem is that the creator of this project doesn't want to store a lot of dates and sort of generate everything dynamically, which is what complicates this a lot.
... complicates this a lot.
You can say that again!  Thanks for the points and good luck with the project, ~Ray
Avatar of t3chguy

ASKER

Thanks, I'll need it!