I ♥ #RunKeeper

Since more than five months now, I’m a very loyal, energized user of RunKeeper. Both the site and the app have compelling, best-designed UIs, functionally and graphically, a pleasure to work with. I’ve tried many other apps out there, including Endomondo and MapMyRUN, but I keep coming back to RunKeeper.

In some strange kind of way RunKeeper acts as a personal motivator and gets me going. Submitting an activity after the hard work of completing a workout seems somehow compulsively rewarding.

App

The free RunKeeper Pro app (available for iPhone, Android and Windows Phone 7) lists activities and allows to edit or create them manually. In practice, I use the app merely as a GPS activity tracking tool. It’s my regular partner for running and cycling activities. Unlike the Garmins and Polars out there, the process of tracking activities with RunKeeper is ultimately simple: push the Start button when beginning an activity and push the End button when completing. All data is automatically uploaded over 3G or WiFi (whatever is available) to the site. There is no need in synchronizing through a PC, messing with cables etc. Convenience for the win.

Whilst sporting, you can listen to music playlists, predefined or randomly generated. You can walk, run or cycle routes, which can be found on the website or which you can create and publicize yourself.

The app comes with audio cues, configurable via de settings tab. During a workout, RunKeeper keeps you informed of key statistics such as time, distance, average pace and speed. These audio cues can be played on demand (by shaking your phone) or periodically, i.e. at a timed interval (e.g. every 15 minutes) or distance (e.g. every 5 kilometers) – or a combination of both.

And yes, battery consumption on my iPhone 4 is very acceptable if the RunKeeper app runs in the background.

Site

The website is the main place where you’ll find stuff like recorded activities, reports, routes and races. It groups all of your sporting activities by month. You can easily track and set your goals. Reports are rather limited unless you upgrade to RunKeeper Elite, after which you’ll be granted discounts on FitnessClasses (coached workout courses) too.

You can edit and share existing activities and enter activities manually. I always enter my swimming activities manually on the site.

Social

RunKeeper has a social aspect: street teams connect RunKeeper users with whom you can complete workouts or comment on completed sporting activities. You can opt to share your activities including a map with street team members only rather than publicly for privacy reasons.

Upon completing and submitting activities, RunKeeper allows you to push updates to Twitter and Facebook.

Foursquare

Connect your RunKeeper account with Foursquare and you can earn up to 4 compelling, RunKeeper-exclusive badges by achieving different milestones.

  1. Warm Up – Rewarded when completing your first 3 activities
  2. 5K – Upon completing a 5km GPS tracked running activity
  3. 26.2 – Acquired when completing a marathon
  4. Over Achiever – When having completed 5 different activity types

Feature Requests

RunKeeper brings improvements and new features quite regularly; nevertheless here’s my personal feature whishlist for the iPhone app:

  • Suggest calories burned when entering an activity manually (calculated by taking the type of activity, duration and weight into consideration)
  • Optionally push a tweet when starting an activity; not only when completing an activity
  • I would like to use RunKeeper to keep track of all my sporting activities. So add more (team) sports, even “distantless activities” such as tennis, badminton, soccer, basketball, etc.
  • Turn-by-turn navigation instructions on routes would be a killer feature and make you think twice before buying a Garmin EDGE.

And last but not least… It would be very cool if Garmin Connect and Polar Personal Trainer could talk directly to RunKeeper or vice versa. If you’re sporting with an advanced Garmin, Polar or other device and still want to keep track of a wider range of activities centralized in RunKeeper, it is quite cumbersome having to…:

  1. Upload activity data to Garmin or Polar
  2. Download GPX/CTX file to computer
  3. Create a new activity in RunKeeper manually
  4. Upload GPX/CTX or other file back to RunKeeper

So if all parties could provide a nice API, it would make sports tracking life much easier.

Advertisements

Automatically Refreshing a Background Image

Example

Take a look at http://www.ktown.be as an example. This simple web page has a background image (taken from a webcam located in the city of Kortrijk), stretched to fill the entire page. The background is automatically refreshed every 15 seconds – smoothly, without flickering.

Stretching

Stretching the image to cover the entire background is achieved through simple HTML and CSS. The background image imgBackground is contained in a div divBackground, resized to fit the entire page.

body {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
}


#divBackground {
background-image: url(http://193.190.76.133:8080/record/current.jpg)
position: absolute;
}


img#imgBackground {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}

The only drawback in the width: 100% and height: 100% combination, is that the aspect ratio of the original image is not maintained, which is no big deal in this case.

Refreshing

Automatically reloading the image is plain simple too, however keep in mind that:

  1. We don’t want flickering to occur, neither do we want to notice the reloading process (block by block)
  2. In most cases, the browser will reuse the image stored in its cache, in other words we have to use a trick to prevent this behaviour

In order to refresh the background without having the browser reloading the image from its cache, concatenate the request uri with the output of a call to Date.getTime(), which returns the number of milliseconds passed since January 1, 1970. Alternatively, a random number could be used. By doing so, the browser is fooled into thinking that it is a different image being requested that the one stored in cache.

<head>
...
<script type="text/javascript">
function reloadBackground() {
url = "http://193.190.76.133:8080/record/current.jpg?" + new Date().getTime();
img = document.getElementById("imgBackground")
img.src = url;
}
</head>

Next, the above function is to be executed by a timer, initiated in the body onLoad event with a call to Window.setInterval(). Pass the name of the JavaScript function reloadBackground() as the first and the number of milliseconds as the second argument – e.g. 15000 for 15 seconds.

onLoad="setInterval('reloadBackground()',15000)">

That’s it. Using a combination of some simple HTML, CSS and JavaScript basics, the background image is automagically and smoothly refreshed.

No Compatible Drives in CDBurnerXP

I’ve been using CDBurnerXP since day one. It’s a no-nonsense CD/DVD/ISO burning tool for Windows and it’s freeware. It supports Blu-Ray and HD-DVDs.

On my HP 620 laptop with Windows 7 Professional 64-bit, version 4.3.8.2568 of CDBurnerXP doesn’t recognize the internal burner, an HP DVDRAM GT30L according to devmgmt.msc, although it is listed as compatible here. The target device for the burning process drop-down list doesn’t contain the burner but reads No compatible drives instead.

Even though I’m quite sure that the problem did not exist with previous versions of CDBurnerXP, the issue is driver related – as is often the case.

In my case, the solution was to download and install Intel’s Matrix Storage Manager driver (which is available on HP’s support site). Reboot and the DVDRAM GT30L burner appears in CDBurnerXP.

Command Line Goodies – Part II

As a sequel to Command Line Goodies – Part I, here’s another trio of helpful tools I quite often use.

Nslookup

Use Nslookup to query DNS servers. The following example will retrieve all DNS records from the primary DNS server as configured in TCP/IP settings (or obtained by the DHCP lease):

nslookup -"set q=any" google.com

Robocopy

An all time classic, Robocopy won’t disappear from batch files pretty soon. Did you know it’s also possible to move – rather than copy – files? For instance, this line moves *.log files older than 100 days from e:\log to an archive directory e:\archive:

robocopy e:\log e:\archive *.log /minage:100 /mov

Grep

Grep is a powerful command-line text-search utility originally written for Unix. Fortunately, there are various Win32 ports available such as Grep for Windows or this older port by Tim Charron.

Just one simple example of what grep can do. The following line takes the two leading lines from all *.log files in e:\log containing a case-insensitive “START LOGON” and dumps the output in *.tmp files:

for %f in (e:\*.log) do grep -i -B 2 -e "START LOGON" "%f" > "%f.tmp"

Check out 15 Practical Grep Command Examples In Linux / UNIX for more examples.

Parsing PowerShell Output from VBScript

You might have a case of a mixed VBScript/PowerShell implementation – for instance a customized MDT script calling a PowerShell cmdlet. No problem, but the tricky part lies in parsing the output i.e. the text PowerShell sends to the console; not the exit code.

Bottom line is: if you don’t close the standard input before reading the standard output, your VBScript will stall and wait forever.

Here’s a VBScript code snippet which calls PowerShell to query all running services and then echoes the result:

Option Explicit

Dim ps: ps = "powershell.exe -command Get-Service | Where-Object {$_.Status -eq 'Running'} | Foreach-Object {$_.Name}"
Dim sh: Set sh = CreateObject("WScript.Shell")
Dim exec: Set exec = sh.Exec(ps)

'-- Close standard input before reading standard output!
exec.StdIn.Close()

WScript.Echo exec.StdOut.ReadAll()

So don’t forget to include StdIn.Close() or the script will simply not terminate.

Changing GRUB’s Default OS

Suppose you’ve got a dual boot configuration with Windows 7 (the primary OS) and Ubuntu 11.04 (installed afterwards). The Ubuntu installer detects the Windows partition(s) and – depending on the configuration you choose – it will install the GRUB boot loader with Ubuntu as the default OS.

Here’s how to change the default OS to Windows 7.

Step 1

Note the position of the Windows 7 entry in GRUB’s menu when booting. Let’s assume it’s the 5th in line.

Step 2

Boot into Ubuntu, start a terminal, take root privileges:

sudo -s

Edit the file grub in /etc/default:

nano /etc/default/grub


Change GRUB_DEFAULT into your choice (the 1st position starts at 0). If Windows 7 is the 5th boot entry, change the line into GRUB_DEFAULT=4.

Ctrl+X, Yes to save and exit Nano.

Step 3

Apply the change:

update-grub


Finally, reboot and verify:

shutdown -r now

Checking AD Group Membership from a Batch File

Batch files are not dead. They are not extinct by VBScript or PowerShell. Not yet. Perhaps never. Get over it.

Here’s how to check if a user is member of a given Active Directory group and act upon the result. The principle is straightforward:

1. Parse the output of net user to retrieve the user’s AD groups
2. Use for and find to count the occurrence of a given group
3. A counter > 0 implies membership

Example:

@echo off
cls
set i=0
set group=SomeAdGroup
set user=%username%
echo Checking if %user% is member of %group%...
for /f %%f in ('"net user %user% /domain | findstr /i %group%"') do set /a i=%i%+1
if %i% gtr 0 (goto :member)
:nomember
echo %user% is not member of %group%
goto :end
:member
echo %user% is member of %group%
:end

Caveats! The above example is not bulletproof:

1. False positives occur when there are groups which contain part of the name of another group – e.g. checking membership of an AD group “ITDep” will return true even if the user is not a member of that specific group but (s)he is member of a group called “ITDepartement”.
2. The net user command has a problem with lengthy group names and truncates values in its output.

SQL Server 2008 R2 – Version string portion was too short or too long

An enterprise-wide rollout of Microsoft SQL Server 2008 R2 Developer Edition can result in serious headaches. On Windows XP SP3 clients, the installer may abort with a System.ArgumentException error: Version string portion was too short or too long. The logfiles residing in %ProgramFiles%\Microsoft SQL Server\100\Setup Bootstrap\Log reveal that the error occurs during the RunDiscoveryAction phase:

Running Action: RunDiscoveryAction
Running discovery on local machine
Error: Action "Microsoft.SqlServer.Configuration.SetupExtension.RunDiscoveryAction" threw an exception during execution.
Microsoft.SqlServer.Setup.Chainer.Workflow.ActionExecutionException: Version string portion was too short or too long. ---> System.ArgumentException: Version string portion was too short or too long.
at System.Version..ctor(String version)
at Microsoft.SqlServer.Discovery.Sql80DetectionInterface.GetFeatureProperties(String instanceName, List`1& outVal)
at Microsoft.SqlServer.Discovery.Sql80Discovery.EnumerateFeatureProperties(String instanceName, List`1& outVal)

Diagnosis indicates that most of the failures occur on clients where previously SQL Server 2000 (aka version 8) Analysis Services (SSAS) had been installed (or still is installed), confirmed by Sql80DetectionInterface in the log file.

Some refer to KB973301 and suggest to create or modify some registry keys related to SQL Server 2000, but that might not remediate the problem.

Even uninstalling SSAS 2000 may not always help as often garbage is left which is picked up by the SQL Server 2008 R2 installer, resulting in the infamous error.

So here’s a working scenario to forcefully remove SQL Server 2000 Analysis Services by:

1) Stopping and removing the MSSQLServerOLAPService service
sc stop MSSQLServerOLAPService
sc delete MSSQLServerOLAPService

2) Deleting the local MsOLAPRepository$ share
net share MsOLAPRepository$ /d

3) Removing the SQL Server 2000 installation directory
rd /s /q "%programfiles%\Microsoft SQL Server\80"

After which SQL Server 2008 R2 installs successfully.

Finding the Closest Domain Controller

The site topology in Active Directory is what determines the “closest” domain controller. The DC used to authenticate with, is available in the %logonserver% environment variable after a successful logon.

In some scenarios you might want to know the closest DC in advance. One example is the phase of joining a new machine in AD during an automated workstation (OS) deployment. You’ll probably want to construct an LDAP path (containing the destination OU for the workstation) including the closest DC to speed up the process.

Bye bye IADsTools.dll

On Windows XP, DsGetDcName() exposed by IADsTools.dll could be used to retrieve the closest DC. (Un)fortunately, it is no longer supported since Windows Vista/2008.

RSAT

On a Windows Vista/7 box with the ActiveDirectory module for PowerShell available (which comes with the Remote Server Administration Tools pack), the process of querying the closest DC is straightforward: simply use the Get-ADDomainController cmdlet. Example:

PS C:\> Import-Module ActiveDirectory
PS C:\> $dc = Get-ADDomainController -DomainName yourdomain.local -Discover -NextClosestSite
PS C:\> $dc.Name

System.DirectoryServices

If RSAT is not available, you’ll have to rely on functionality exposed by the .NET framework in the System.DirectoryServices namespace. It takes a little more work to achieve the same result:

PS C:\> $type = [System.DirectoryServices.ActiveDirectory.DirectoryContextType]"Domain"
PS C:\> $context = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext($type, "yourdomain.local", "domainuser", "password")
PS C:\> $domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($context)
PS C:\> $domain.FindDomainController().Name

Deploying SQL Server 2008 R2 Silently

The installation sources of SQL Server aren’t composed of one or two simple MSIs nor is it a software you want to repackage. The most obvious way to deploy SQL Server 2008 R2 is to a use the command line (setup.exe) in combination with a configuration file. In this example we’re deploying SQL Server 2008 R2 Developer Edition to desktops (none of the engines; only the tools). Steps to follow:

1. Generate the configuration file

Creating the configuration file is a straightforward process. Simply walk through the setup wizard, selecting required features and specifying options/settings as you would normally do during a manual installation. When the Ready to Install window appears, note the full path where ConfigurationFile.ini resides, copy the file and cancel the setup wizard.

SQL Server 2008 R2 Configuration File

2. Modify the configuration file

Open the INI file with your favorite text editor. You might want to modify/add:

  • QUIET="True" for a completely silent installation
  • IACCEPTSQLSERVERLICENSETERMS="True" (required)
  • PID="productkey" (unless working with volume licensing or if deploying an evaluation version)
  • Remove (comment) UIMODE="Normal" (not allowed with /quiet)
  • Remove (comment) INSTALLSHAREDDIR and INSTALLSHAREDDIR (so the default %PROGRAMFILES% paths will be used which might not refer to the drive/paths specified in the wizard)

Note that each parameter can be specified as an argument for setup.exe too. Arguments specified at the command line override those in the INI file.

3. Installation

Finally, execute setup.exe with the full path to the configuration file:

setup.exe /ConfigurationFile=\ConfigurationFile.ini [/hideconsole]

If completed successfully the installer will return 0 or 3010 (reboot required). During installation, detailed logs are created in %program files%\microsoft sql server\100\setup bootstrap\log.

Tip: prior to launching the installation, it’s not a bad idea to install some potentially missing prerequisites in advance, such as the .NET Framework 3.5 SP1 and Windows Installer 4.5 (which requires a reboot), especially if you already have separate packages for these products.

Resources

How to: Install SQL Server 2008 R2 from the Command Prompt
http://msdn.microsoft.com/en-us/library/ms144259.aspx

How to: Install SQL Server 2008 R2 Using a Configuration File
http://msdn.microsoft.com/en-us/library/dd239405.aspx

%d bloggers like this: