October 2006 - Posts

Who would have ever thought that people would think comments are a bad thing?  That is the opinion that you get about comments; actually, Martin Fowler said that comments aren't bad; however, if the comments are explaining what your code is doing, without your code being readable enough to handle it's own, it's time for a refactoring.  Comments can be a great way to prototype changes, or to think out your design as well, as Steve McConnell said in Code Complete.  He further said to leave in the comments, even though they will be very verbose.  You will see in my code, that I usually follow this approach.

Whether that is right or wrong, your code should be readable and understandable.  With custom objects, especially framework or third-party components, that can't always be the case, but it should be as readable and understandable as possible.  I still employ comments, and though your code should be readable throughout, I still do it.
 

Posted by bmains | 1 comment(s)
Filed under:

The idea about creating the user interface in an XP project isn't known.  The biggest challenge, when designing a tool, is how it will look.  Though you do mentally design it, and gather what the interface should look like and function from requirements, it isn't always known what to do with it.  If you are using an XP methodology, you don't have to know; that's part of the refactoring process.  When the customer is readily involved, you can show them different designs, and once they can see how it looks, they can make better decisions about the appearance.

It is hard to understand how a tool will work when you don't have the ability to see it for yourself.  Even with systems with a lot of up-front design, you tend to still say to your self "I wish they would have done that differently."  Plus, if you are coding abstract enough, you should be able to reuse the same code with different interface controls, with minor rework (this is a major benefit of MVC).  It's a lot tougher when you have so much invested in an interface, especially when embedding your code in the interface.
 

Posted by bmains | with no comments
Filed under:

I actually really liked the idea of the customer being readily involved in the project.  If the customer was easily accessible, any ideas/problems can be directly bounced off of them, guiding the project to a more successful and welcomed completion.  This can be really hard though.

Let's face it; the customer sitting with the developer won't happen in some environments.  I'm on a project where I have managers as the users, and so it is hard to get them in a meeting, let alone working with them directly.  Sometimes, the environment causes this.

But, this approach is handy, and even if you aren't using an agile/XP approach, it is a good idea to regularly meet with the users and understand their needs.

Posted by bmains | with no comments
Filed under:

In the downtime on the project, I wanted to talk about a tenet of XP programming, which is paired programming.  The general idea is that if we worked in pairs, we could bounce ideas off of each other.  Code would be better, because we could double-check each other's thought process, as well as literally checking the code.

In addition, it helps with those who are strong in one area and weak in another, because the other person could be the opposite way.  So, the concept of pair programming can be very helpful.

But this has a negative connotation; it could be seen as inefficient; that is a lot of work for the two to handle, instead of both working on the project.  Most people have the philosophy that more time coding means more of the project is done; but what XP'ers are saying is that by ensuring you do it right the first time, and testing that it does work ahead of time, you ensure that the code will not have to be fixed later (unless the user changes their mind that it affects that piece of code), and it will be well refactored, making the design easier.

Still, it can be hard to work with someone, especially with conflicting ideas/opinions.  I like the idea of working together; however, if paired programming is not for you, there are other agile methodologies that may be more suitable.

Posted by bmains | 1 comment(s)
Filed under:

I've decided to stop there and release the product.  I've placed the zip file that contains the executable and DLL's on CodePlex (under planned releases, or link directly).    I haven't gotten the source code out there yet; I will when I can overcome some technical difficulties.  What I plan to do is this:  I'm going to let this project go for a couple of months (or so; I don't have an exact figure in mind).  I ask that all of you download it, run it, and find any bugs associated with the tool.  Also, figure out things that you would like to see, and enter bugs/features into the Issue Tracker tool.  There is also a forum where you can post about the project; I will try to answer questions if I can, and expand the list of forums that are out there.

Furthermore, when using it, whether the feature is large or small, if you believe it is beneficial, put it in the issue tracker.  You are my user base, and this will craft the shape of the project to come.

Thanks for reading the blog, and I hope it has been of benefit to you.  Any future posting will be minimal until it starts up again, so it will be on hiatus. When I get the code out there, I'll post and let you know.
 

Posted by bmains | with no comments

I found a flaw in my test, which was less detectable since I didn't have a good range test.  The values were six months apart from each other, and I didn't realize that the partial value was calculated incorrectly.  It should have been instead this:

TimeSpan fullValue = completionDate.Subtract(lastCompletionDate);
TimeSpan partialValue = currentDate.Subtract(lastCompletionDate);

I was calculating the higher end of the value (what was to come instead of what has elapsed.  So, my final value was calculated in the Task's PercentageDue property defined below.  I took the code from the test and adapted, refactoring the variable names to make more sense:

public double PercentageDue
{
    get
    {
        //Return 100 if no due date
        if (!this.Attributes.Contains("DueDate"))
            return 100;
        //Get the date values
        DateTime dueDate = this.GetAttributeValue<DateTime>("DueDate");
        DateTime currentDate = DateTime.Today;
        //If the due date is less than the current date (overdue), return 100
        if (dueDate < currentDate)
            return 100;
       
        DateTime originDate;
        //If the last completed date has been provided, use this value
        if (this.Attributes.Contains("LastCompletedDate"))
            originDate = this.GetAttributeValue<DateTime>("LastCompletedDate");
        //Otherwise, first time so use the creation date
        else
            originDate = this.GetAttributeValue<DateTime>("CreationDate");

        //Get the time span difference between the date values
        TimeSpan totalTime = dueDate.Subtract(originDate);
        TimeSpan timeElapsed = currentDate.Subtract(originDate);
        //Get the days value in double form
        double totalDays = Convert.ToDouble(totalTime.Days);
        double elapsedDays = Convert.ToDouble(timeElapsed.Days);

        //Get the percentage value of dividing partial value
        //by the full value; multiply by 100
        return Math.Round((elapsedDays / totalDays) * 100, 1);
    }
}

I also return a rounded value, instead of a true floating value.

Adding another column to the task list, I also provided the means to add the new field to the subitems list, so it is displayed in the interface.  In addition, the due date calculation turned out to be complex, so I added another method to render it in the list view.  If the current date is overdue, I wanted it to say "Overdue"; however, if the due date attribute isn't provided, I wanted it to say "Complete", and lastly, if the value is there, write it.

private string RenderDueDate(Task task)
{
    if (task.Attributes.Contains("DueDate"))
    {
        DateTime dueDate = task.GetAttributeValue<DateTime>("DueDate");
        if (dueDate < DateTime.Today)
            return "Overdue";
        else
            return dueDate.ToShortDateString();
    }
    else
        return "Completed";
}

It is rendered in the list view as such:

internal void UpdateTaskInformation(Task task)
{
    string name = task.Name;
    string category = task.Category;
    bool reoccurring = task.GetAttributeValue<bool>("IsReoccurring");
    string dueDate = this.RenderDueDate(task);
    double percentage = task.PercentageDue;

    ListViewItem item = this.FindByTag(task);

    if (item != null)
    {
        item.Text = name;
        item.SubItems[1].Text = category;
        item.SubItems[2].Text = reoccurring.ToString();
        item.SubItems[3].Text = dueDate;
        item.SubItems[4].Text = percentage.ToString();
    }
}

I created two tests to test the common types of progression; the first will use date/time comparisons, where the next one will use numerical comparisons.  The first is in the case of calculating a reoccurring task, where the other option is for calculating custom measurements, as we will see.  I created the first test as below:

[Test()]
public void TestDueDatePercentage()
{
    DateTime completionDate = new DateTime(2007, 1, 1);
    DateTime lastCompletionDate = new DateTime(2006, 6, 1);
    DateTime currentDate = new DateTime(2006, 9, 1);

    //Get the time span difference between the date values
    TimeSpan fullValue = completionDate.Subtract(lastCompletionDate);
    TimeSpan partialValue = completionDate.Subtract(currentDate);

    //Make sure the values and day counts are different, with full value being larger
    Assert.AreNotSame(fullValue, partialValue);
    Assert.AreNotEqual(fullValue.Days, partialValue.Days);
    Assert.Greater(fullValue.Days, partialValue.Days);

    double fullDays = Convert.ToDouble(fullValue.Days);
    double partialDays = Convert.ToDouble(partialValue.Days);

    //Get the percentage value of dividing partial value by the full value
    double percentage = partialDays / fullDays;
   
    Console.WriteLine("TestDueDatePercentage percentage:  " + percentage.ToString());
    //Make sure the value is within a particular range
    Assert.IsTrue(percentage > 0.40);
    Assert.IsTrue(percentage < 0.60);
}

I created a task that was last completed on 6/1/2006 and will be due on 1/1/2007.  It is currently 9/1/2006 in this scenario, and I test to see that whenever I get the days, that I can calculate the percentage.  I get a value in the .57 range, and so this test worked; this is the reason for the big range, because the actual date comparison value will vary greatly.  Also, to actually get a decimal value, you have to use double or float.  I use double for ease of use, and this works great for this purpose (int returns 0 or 1).  The next test compares integer comparison.

[Test()]
public void TestMeasurementPercentage()
{
    double fullValue = 5000;
    double partialValue = 2500;

    //Calculate the current percentage
    double percentage = partialValue / fullValue;

    Console.WriteLine("TestMeasurementPercentage percentage: " + percentage.ToString());
    //Make sure the value is within a particular range
    Assert.IsTrue(percentage > 0.49);
    Assert.IsTrue(percentage < 0.51);
}

I am comparing a task that is halfway complete, and so I create the percentage and test the range.  I use a +1 approach because it could be off slightly; decimal point values have a habit of being slightly off, even though the measurement should be exact.  This test also works.

Posted by bmains | with no comments

We've completed another iteration of the code.  Moving forward, the next stories selected will be:

  • A progression value is tracked for each task
  • Reminders can be setup to notify the user of a task
  • Custom reoccurrence measurements can be managed
These are pretty complex tasks, which is the reason why only three were selected.  Actually, this is pretty aggressive, since the previous iteration had a velocity of 6.  This is having a high expectation on the team, but I believe we can accomplish these tasks in the next iterative cycle.
Posted by bmains | with no comments
Filed under: