Uncategorized

Human-Friendly Dates


#region Human Friendly Dates

///

/// Takes instances of dates and converts them to more human-readable interpretations.
///

///
///
///
public static string ToHumanFriendlyDate(this DateTime date, bool neverShowSeconds = true)
{
TimeSpan ts = DateTime.Now.Subtract(date);
return ToHumanFriendlyDate(ts, neverShowSeconds);
}

///

/// Takes instances of dates and converts them to more human-readable interpretations.
///

///
///
///
public static string ToHumanFriendlyDate(this DateTime? date, bool neverShowSeconds = true)
{
if (date == null)
return null;
return ToHumanFriendlyDate(date.Value, neverShowSeconds);
}

///

/// Takes instances of dates and converts them to more human-readable interpretations.
///

///
///
///
public static string ToHumanFriendlyDate(this TimeSpan timespan, bool neverShowSeconds = true)
{
/* Examples of conversions:
* < 1m : Less than a minute [ago|to go]
* 1: A minute [ago|to go]
* 2: A couple minutes [ago|to go]
* 3-4: A few minutes [ago|to go]
* 5+: [min] minutes [ago|to go]
* 1 hour: An hour ago/to go
* 2 hours: A couple hours [ago/to go]
* Yesterday: Yesterday at 12:30 PM
* Tomorrow: Tomorrow at 12:30 pm
*/
bool isInFuture = timespan.TotalMinutes != Math.Abs(timespan.TotalMinutes);
Func endQualifier = () => isInFuture ? "to go" : "ago";
Func formatDateFunc = date => date.ToShortDateString().Replace("/" + DateTime.Now.Year, "");
if (Math.Abs(timespan.TotalSeconds) < 5)
{
return "A few seconds " + endQualifier();
}
else if (Math.Abs(timespan.TotalSeconds) < 60)
{
return "Less than a minute " + endQualifier();
}
else if (Math.Abs(timespan.TotalMinutes) < 3)
{
return "A couple minutes " + endQualifier();
}
else if (Math.Abs(timespan.TotalMinutes) < 5)
{
return "A few minutes " + endQualifier();
}
else if (Math.Abs(timespan.TotalMinutes) < 55)
{
return timespan.Minutes.ToString() + " minutes " + endQualifier();
}
else if (Math.Abs(timespan.TotalHours) < 1)
{
return "Less than an hour " + endQualifier();
}
else if (Math.Abs(timespan.TotalHours) < 2 && Math.Abs(timespan.Hours) == 1)
{
return "An hour " + endQualifier();
}
else if (Math.Abs(timespan.TotalHours) < 3)
{
return "A couple hours " + endQualifier();
}
else if (Math.Abs(timespan.TotalHours) < 5)
{
return "A few hours " + endQualifier();
}
else
{
string day;
DateTime d = DateTime.Now.Subtract(timespan);
if (Math.Abs(timespan.TotalDays) < 2)
{
if (d.Day DateTime.Now.Day)
{
day = "Tomorrow";
}
else
{
day = "Today";
}
}
else
{
day = formatDateFunc(d);
}
if (neverShowSeconds)
{
return day + ", " + d.ToString("h:mm tt").Replace(":00", "");
}
else
{
return day + ", " + d.ToShortTimeString().Replace(":00", "");
}
}
}

///

/// Takes instances of dates and converts them to more human-readable interpretations.
///

///
///
///
public static string ToHumanFriendlyDate(this TimeSpan? timespan, bool neverShowSeconds = true)
{
if (timespan == null)
return null;
return ToHumanFriendlyDate(timespan.Value, neverShowSeconds);
}

#endregion

Advertisement
Uncategorized

Get enums by index, get index of an enum value in an enum

I like using enums for simple selections. I feel it makes my code clean. Over time you may feel that parts of your program have “outgrown” enums and you may need to switch to classes to handle extra logic. You can, but with attributes, you do not have to. If a parameter must be one of a few choices, even if it’s nullable, then use an enum.

I’m writing an iPhone app using Monotouch where I am displaying enum options to be picked from a list. The functions I am listing below help greatly in dealing with indices on enums.

 

public static TEnum GetEnumByIndex(int index)
{
var etype = typeof(TEnum);
if (!etype.IsEnum)
throw new ArgumentException(“‘TEnum’ must be of an enum type.”);
var enums = Enum.GetValues(typeof(TEnum));
if (index > enums.Length)
throw new ArgumentOutOfRangeException(“index”);
TEnum option = (TEnum)enums.GetValue(index);
return option;
}

public static int? GetIndexOfEnumValue(TEnum value)
{
var etype = typeof(TEnum);
if (!etype.IsEnum)
throw new ArgumentException(“‘TEnum’ must be of an enum type.”);
var enums = Enum.GetValues(typeof(TEnum));
int i = 0;
foreach (object e in enums)
{
if (((TEnum)e).Equals(value))
return i;
i++;
}
return null;
}

In other news, my daughter is doing really well with her chemo treatments. Hopefully she will be cancer free in a few years! See my wife’s page detailing my daughter’s battle with Acute Lymphoblastic Leukemia.

Uncategorized

Silverlight, popup windows, and browser/out of browser differences

Opening popup windows in Silverlight is a pain in the rear. However, a way has been found to work around this. Subclass the HyperlinkButton and expose a new method that calls the protected Click() method.


internal class OOBPopup : HyperlinkButton
{
public void Navigate(Uri url)
{
this.NavigateUri = url;
this.TargetName = "_blank";
base.OnClick();
}
}

To use:

new OOBPopup().Navigate(url);

Let’s compare this with the two other options, HtmlPage.Navigate() and HtmlPage.PopupWindow(). Everything below is on Windows 7 64-bit. (IB = in browser, OOB = out of browser)

IB IE 9: All three methods work.
IB Safari 5.1.1: Only the OOB popup works.
IB Firefox 7: Only the OOB popup works.
IB Chrome: Only the OOB popup works.
OOB: Only the OOB popup works.

Where a method does not work, either you will get a message saying that the HTML/DOM scripting bridge is disabled, or the browser (especially Safari) will just eat the popup and act like nothing happened.

With the OOBPopup class setting enableHtmlPopups in the OBJECT tag hosting the silverlight app is not required, so it’s a great way to get popup windows to work. The app I am working on makes heavy use of SSRS so popup windows are necessary.

Uncategorized

RIA Services and Timestamp/Concurrency Field Gotcha

I’m working with a table that has a timestamp column in EF. I’m using this to detect when multiple people are editing the same data so that one person’s changes don’t get overwritten by another user. In the EF designer, you would mark this property as StoreGeneratedPattern=Computed and ConcurrencyMode=Fixed, so that the code gen knows to generate a read-only field that gets roundtripped to the server on updates. If, during an update, the timestamp value that gets roundtripped to the server differs from the one in the database, then an OptimisticConcurrencyException is thrown so you have a chance to get the stored version of the data and possibly pop up a dialog that allows you to merge the changes together.

However, I have come across a gotcha when working with RIA services. Since the timestamp field is not NULLable, RIA services marks it as [Required] and [Editable(false)] when generating the code. When creating a new class to add to the data store, this ends up messing with validation, so in my datagrid it pops up with “Timestamp field is required”. You can’t set the field yourself, as it will throw an exception saying that the field is read-only.

A solution is found in the comments here for Linq-to-SQL, so here’s how to do it for EF.

I didn’t bother using the EF designer, I right-clicked the file in VS and clicked “Open With…”, then clicked XML editor. This brings up the XML schema that’s used to generate all of the code. Scroll down until you can find the class, mine was <EntityType Name="Class">. Find the property for your timestamp, mine was <PropertyType Name="Timestamp" ConcurrencyMode="Fixed" Nullable="false" ...>. Change that Nullable="false" to true, and save. The RIA services generated code will no longer require you to fill in the timestamp, and the database will handle creating the timestamp for you when the object is persisted to the database.

However, there is one more gotcha – it’s still marked as a required field. There may be a better way to accomplish this, but what I did was this: I have a field called CreationTimestamp, so I used the partial OnCreationTimestamp() method:

partial void OnCreationTimestampChanged()
{
if (Timestamp == null)
Timestamp = new byte[] { };
}

This fixes the issue, for now.

Anyone know of a better way?

Uncategorized

FizzBuzz in one line C# Linq

The FizzBuzz problem is this:
– Print numbers from 1 to 100.
– Print “Fizz” instead of the number if the current number is evenly divisible by 3.
– Print “Buzz” instead of the number if the current number is evenly divisible by 5.
– Print “FizzBuzz” instead of the number if the current number is evenly divisible by both 3 and 5.

Here’s the FizzBuzz problem, expressed as a single line in C# using Linq:


Enumerable.Range(1, 100).ToList().ForEach(i => Console.WriteLine((i % 3 == 0 && i % 5 == 0) ? "FizzBuzz" : (i % 5 == 0) ? "Buzz" : (i % 3 == 0) ? "Fizz" : i.ToString()));

It took about 45 seconds to write the code. =]

Uncategorized

Run PowerShell scripts within Silverlight 4 OOB Elevated Permissions

You have to run your SL app with elevated permissions out-of-browser, but this will allow you to query Powershell. You’ll have to parse the output yourself.

namespace TestPowershellScripting
{
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices.Automation;

///

/// Powershell runner for Silverlight applications.
/// Requires that OOB and elevated permissions are active.
///

public class PowerShellSL
{

///

/// Name of the PowerShell executable.
///

private const string PowerShellExecutable = "powershell.exe";

///

/// Specifies whether to include user profile information as part of the PowerShell
/// executing context. When set to false, PowerShell runs faster, but does not have
/// access to user-level environment variables.
///

public bool UseProfile { get; set; }

///

/// Runs a PowerShell script and returns the output as a string.
/// In the PowerShell script, use WriteHost to write output to standard output.
/// The script is written to standard input and run using the WScript library.
///

/// The PowerShell script to run, expressed as a string.
///
/// A FileNotFoundException is thrown if Powershell cannot be run.
/// An InvalidOperationException is thrown if the automation factory is unavailable.
public string Run(string script) {

// Check to see if stuff is available
if (!AutomationFactory.IsAvailable)
throw new InvalidOperationException("AutomationFactory is unavailable. Program must be run as out-of-browser with elevated permissions.");

var shell = AutomationFactory.CreateObject("WScript.Shell");
string cmd = PowerShellExecutable + " -NonInteractive " + (UseProfile ? "" : "-NoProfile") + " -WindowStyle Hidden -Command -";
string output = "";

dynamic result = null;
try {
result = shell.Exec(cmd);
result.StdIn.Write(script);
result.StdIn.Close();

while (!result.StdOut.AtEndOfStream) {
output += result.StdOut.ReadLine() + "\r\n";
}
} catch (FileNotFoundException fnfex) {
throw new FileNotFoundException("PowerShell is not installed on the local computer.", fnfex);

}

return output;
}

///

/// Runs a PowerShell script asynchronously (using a BackgroundWorker) and returns the output as a string.
/// When results are available, the delegate 'results' is called with the program output.
/// In the PowerShell script, use WriteHost to write output to standard output.
/// The script is written to standard input and run using the WScript library.
///

/// The PowerShell script to run, expressed as a string.
/// The delegate method to call when results have been returned.
/// A FileNotFoundException is thrown if Powershell cannot be run.
/// An InvalidOperationException is thrown if the automation factory is unavailable.
public void RunAsync(string script, Action results) {
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(
(sender, e) => {
e.Result = this.Run((string)e.Argument);
}
);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
(sender, e) => {
if (results != null)
results((string)e.Result);
}
);
bw.RunWorkerAsync(script);
}

}

}

Uncategorized

JQuery Input Field Magic

I’m working on a form that takes in names of children to be added as visitors to an organization. The form has 20 rows of input fields enclosed in a TBODY, so I know which rows are not header rows. I don’t want to show all 20 rows, since most people have on average three children, but I want to be able to handle a large group of children in a family, in case the Duggars move here or something. So, the following javascript hides all fields except the first and except any that are pre-filled (in the event that form validation fails).


<script type="text/javascript">

var $children = $("#children");
$children.find("tbody tr:not(:first):not(:has(input[value]))").hide();
$children.append("

");
$("#addChild").click(function () {
var nextrow = $children.find("tbody tr:hidden:first")[0];
$(nextrow).show();
nextrow = $children.find("tbody tr:hidden:first")[0];
if (nextrow == null) {
// get rid of add button since there are no more hidden rows to display
$("#addChild").remove();
}
});

</script>

Let’s look at this line:

$children.find("tbody tr:not(:first):not(:has(input[value]))").hide();

I’m basically saying, find all TR (table rows) inside of a TBODY tag, excluding the first and excluding any rows that contain INPUT tags that contain a value.

The next line adds a TFOOT tag to the end of the table, and adds a button inside a row called addChild.

The next code block acts when the button is clicked. It finds the next hidden row, and displays it. It then finds the next hidden row after that, and if one is not found, the button is removed.

Uncategorized

My gripes with the Facebook iPhone app

I use the Facebook iPhone app all the time, far more than I use the web site. The app is great, except for a couple flaws, one which I think is a major issue.

There is no way to block posts from other people like you can with the web site. I had this group “liked” called No Stressss, which had some cool stuff occasionally, but most of the time posts were made in Arabic and there would be 10 posts in a row, as if the person was copying-and-pasting and posting stuff rapid-fire. I blocked their posts from the web site, but they still show up on my phone, since the phone app doesn’t seem to follow the same post display rules as the web site does. Since I use Facebook on my phone more than I do the regular web site, I had to “unlike” them so the barrage of posts didn’t show up on my phone anymore. I didn’t understand how to unlike someone at first, especially since I wasn’t able to see their posts on the web site and you can’t hide posts from people from the phone app, so I had to type the name of the person and find the tiny “unlike” link on the left side to remove them.

That’s no so bad, I guess. There should be a way to block posts and like/unlike people from the phone. That’s not the most annoying issue though… I’ll read Facebook once or twice a day, at most. The way the news feed on the phone is set up is you scroll up and down a “page” of posts. If you pull the list down (scroll up) and then let go, you will refresh the list of posts. If you scroll to the bottom, there is a “Older Posts” button that, when clicked, loads the next page of results and appends it. Super simple, great UI design, and a three year old can figure it out. My gripe is with how arriving posts are handled. It seems the app either polls for new posts on a timer, or there’s a push notification sent when a new post has been posted. If you’re three pages deep, the app’s UI will block/freeze and the list will completely reset to the first page of posts. So, I’m reading a post from my cousin about fist pumping to Jersey Shore and just when I’m about to hit the reply button to call him a dirty little hamster, the UI blocks and the list resets about 5 seconds later.

Why does the list have to reset itself? Basically the list is a collection of posts ordered by date and time descending. Since it’s either polling for new posts or using a push notification to get new posts, why not just push the new results to the top of the stack rather than reset the whole list, so I don’t have to scroll down to the bottom of the list and click Older Posts three times to get back to where I was? If I close and re-open the app, the list can be reset then. I wouldn’t reset the list if the person closed or backgrounded the app within a few minutes.

The Facebook app is indispensable, and aside from these issues, is well designed. It just needs a little more polish to achieve greatness.

Summary:

Posts on phone do not adhere to ignore rules set up on the web site.
There is no way to block posts, or unlike people from the phone app.
When new posts arrive, the list resets itself to the first page, losing your current place.

Uncategorized

Building infrastructure to support one declaration of domain model for use with EF and RIA Services

I’ve been working on a way to declare a domain model on the server, to be used on both the server and on the Silverlight client through RIA services. Believe me when I say that this has been no easy task!

I tried to implement a single shared IDataRepository interface to both the client and server by marking the filename as .shared.cs. The data access methods would use a syntax similar to the following: repo.Get(string[] includeRelatedTables, Action<IQueryable> results), so when the data has been retrieved, it calls the callback with the results. With Silverlight, there is no synchronous database communication, so this syntax is the only one that works. However, that callback method won’t work when using the domain model with MVC… I can’t return a ViewResult to the anonymous function inside.

Another way of doing this would be to declare virtual methods that, on each side (client EF/server RIA) implements its own way of getting the data. Another better solution would be to declare these functions as Func<IQueryable> properties that can be get or set. However, I would run into the same problem as MVC on the server side, where the virtual method or Func would require me to return the results from the data source, and I can’t return from within an anonymous function… so, with this method, I’ve hit a brick wall.

I guess it’s time to install the Async CTP and see if I can make this work without using callbacks. I know projects like CSLA have this covered, but I want to create (or find a good) solution that works right away with my EF entities and RIA entities. The Async CTP should allow me to write my async data call methods like I would with RIA services, so I should be able to get rid of all of the callbacks and have both interfaces return the list of objects directly.

Uncategorized

Attribute for validating when at least one of many properties must be set

Here’s some code for a validation attribute that can be set on a class, when at least one property out of a couple must be set in order to pass validation. A great use for this is when you require either a home phone number or a cell phone number to be set, since you can’t expect all user to have a home phone number, and you can’t expect all users to have a cell phone number either, but you have to know whether it is a home or a cell number.

Enjoy!

///

/// Checks all the properties and returns true if at least one has a value.
/// ErrorMessage string must be specified!
///

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class MustHaveAtLeastOneOf : ValidationAttribute
{
private readonly object _typeId = new object();

public MustHaveAtLeastOneOf(params string[] properties)
: base("") {
MustHaveAtLeastOneOfProperties = properties;
}

public string[] MustHaveAtLeastOneOfProperties { get; private set; }

public override object TypeId {
get {
return _typeId;
}
}

public override string FormatErrorMessage(string name) {
return ErrorMessageString;
}

public override bool IsValid(object value) {
if (ErrorMessageString == null) { throw new ArgumentNullException("ErrorMessage string must be filled in when declaring the attribute."); }
PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
//object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);
//object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value);
//return Object.Equals(originalValue, confirmValue);
bool foundWithValue = false;
foreach (PropertyDescriptor prop in properties) {
if (MustHaveAtLeastOneOfProperties.Contains(prop.Name)) {
object val = prop.GetValue(value);
if (val != null && val.GetType().Name.ToLower().EndsWith("string")) {
if (string.IsNullOrWhiteSpace((string)val)) {
val = null;
}
}
if (val != null) {
foundWithValue = true;
}
}
}
return foundWithValue;
}
}