Baby’s First Domain redesign with Laravel and Bootstrap

Baby’s First Domain redesign with Laravel and Bootstrap

One of my ugliest sites has always been Baby’s First Domain. The background was a muddy greenish color. The blindingly bright orange from the logo wrapped the content of the page. Everything was unstyled and just bleh. To make matters worse, the backend was powered by an ancient version of CodeIgniter. And with 37% of this site’s visitors using a mobile device, the fixed width layout was unacceptable.

The site was originally intended to help you find a domain name for your child. You can then use that domain name for email, a photo gallery, a blog, a link to their wish list at Amazon, whatever. That part of the site has never really turned a profit, so at some point I added baby name meanings. This part of the site is surprisingly popular.

I hope to get the domain name selling portion of the site beefed up so it can take over as the main feature of the site. I think I need to add some instructional videos on how to buy a domain, what to do with it, how easy it is to set up a wish list or photo gallery, that sort of thing.

Before that can happen, I needed a new design for the site using a modern framework. I settled on Laravel for the backend because it has basically won the PHP framework wars. It is easy to use and works well with popular build tools. It uses Composer for package management, so it is easy to pull in useful third-party tools. And everybody uses it so it is easy to find examples for just about anything you could want to do.

For the frontend I went with Bootstrap. I’ve used it a bunch on other sites. It is easy to make responsive sites without monkeying with a bunch of CSS. Its build system makes it easy to customize without digging through thousands of lines of code.

I spent about 3 days working on the new site. I used a few new packages that had some large prerequisites and I’m on incredibly slow DSL, so most of the first day was spent getting my dev environment setup. Oh how I miss cable internet!

The logic for this site is pretty straightforward, and I was able to copy/paste a lot of it from the old site. I saved the logo from the old site for now. My wife is going to convert the baby in the logo into a vector, then I’ll use it and some CSS to make a more appealing logo that loads faster on all devices.

I also added a new search feature. You can type anything you want into the search box. The search page tries to find a baby name that matches what you searched for. If it is a name that works for both genders, it sends you to the most popular gender for that name. If the name isn’t found, it calculates the Levenshtein distance between your search term and all the names in our database, and directs you to the closest match. This makes it really easy to find the baby name meaning you are looking for.

I also use the Levenshtein distance on some of the 404 pages. For example, if someone tries to go the meaning page for Jaycob, the 404 page will suggest Jacob, Yaacob, or Kaylob.

Another new feature I added is a boys and girls name page. On the old site you had to pick the first letter of the baby’s name to get a listing of all names that begin with that letter. You can still do that on the new site, but it is a bit tedious. On the new boys/girls name pages, I display a handful of the most popular names for each letter, with a link to the full listing. This makes it much easier to find common names.

Laravel is an absolute joy to work with. In about 10 minutes I created a command line script that pulls names from the database to automatically create a sitemap. The sitemap is saved in both xml and gz formats. The script is automatically run once a week by Laravel’s scheduler.

There is a lot of room for expansion on this site. I could easily add a baby due date calculator, tie in to various baby name APIs for more data, and perhaps even add templates for baby websites (like an announcement site, photo gallery, etc).

Anyways, I’m pretty happy with it. Only time will tell if the redesign actually increases my revenue or retains visitors better, but I’m optimistic.

Read More

Picking a random date between two dates

Picking a random date between two dates

Many years ago I created the Fake Name Generator, a website that lets you create fake names, addresses, birth dates, and other profile information. I’ve had issues with the birth date component ever since I wrote the dang thing, including a huge bug I fixed a few years ago.

Well today I was informed of another bug. A while back I added the ability for visitors to select an age range for their generated identities. The code that handled this was atrocious: it would figure out the birth year range by subtracting the selected min/max ages from the current year, then pick a random year within that range. It would then pick a random number 1 to 12 to represent the month, a random number 1 to 31 to represent the day of the month, then it would use checkdate() to see if the date is valid (for example, 2/31/1990 would be invalid). If it is invalid, it would try again. Most of the possible generated dates are valid so it only occasional has to try again.

Aside from being ugly, the big problem with this code is that it doesn’t always generate birth dates within the selected age range. Say you want to generate a birth date that would make someone at least 19 years old and no older than 19 years old (i.e., exactly 19 years old). In January most of the generated birth dates will make an 18 year old, in June you’ll have about a 50/50 split of 18 and 19 year olds, and in December most will be 19 year olds. This is because my code was only dealing with the birth year, and wasn’t taking into account the birth day or month in relation to the current date. Not very noticeable if your age range is something like 1 to 100 years old, but very noticeable if it is January 10th and you want a birth date for a 19 year old. Like I said, horrible code.

So here is my latest try. This function takes a minimum and maximum age as parameters, and spits out an array with the birth date and age:

function dateTime($minAge = 19, $maxAge = 85)
{
    // Sanity check
    if(!is_numeric($minAge)) $minAge = 19;
    if(!is_numeric($maxAge)) $maxAge = 85;
    if($minAge > $maxAge) $minAge = $maxAge;

    $tz = new DateTimeZone('US/Eastern');

    $minDate = new DateTime('now', $tz);
    $minDate->modify("-$maxAge years -1 year +1 day");
    $maxDate = new DateTime('now', $tz);
    $maxDate->modify("-$minAge years");

    $minEpoch = $minDate->format('U');
    $maxEpoch = $maxDate->format('U');

    $birthday = new DateTime('@'.mt_rand($minEpoch, $maxEpoch), $tz);
    $today = new DateTime('now', $tz);

    $date = array(
        'year'      => $birthday->format('Y'),
        'month'     => $birthday->format('n'),
        'month-name'=> $birthday->format('F'),
        'day'       => $birthday->format('j'),
        'formatted' => $birthday->format('n/j/Y'),
        'age'       => $today->diff($birthday)->y,
    );

    return $date;
}

The code is a bit long, but is actually pretty straightforward:

First we do a little sanity check. I want my code to return valid data even if someone manages to pass in a bad value, so I check to make sure the min and max ages are numeric and that the min age isn’t larger than the max age. This could be improved, but I think it is good enough for my needs.

Next we create a DateTimeZone object. My server’s time is set to US Eastern, so I want all my dates to use this time zone. This object will get passed into the constructor for DateTime later in the code.

In the next block of code I create two DateTime objects with today’s date. I use the modify method to subtract an appropriate amount of time from each object so I get the minimum and maximum birth date that someone could have and still fall within the desired age range. DateTime makes this easy by allowing things like +1 day. It doesn’t care about plurals, so you can use day and days, year and years interchangeably and inappropriately.

This next bit was a stroke of genius (which I later discovered is what some experts online would consider “the obvious way”). I convert the minimum and maximum dates into Unix timestamps, which are just integers. So if we are generating an identity for someone between 20 and 50 years old, the minimum date would be something like -219961293 and the maximum would be 758259507.

Now that our dates are just integers, picking a random date between the two is easy. I create a new DateTime object with the date set to a random Unix timestamp between the minimum and maximum Unix timestamps I came up with in the previous step. I then create another DateTime object, $today, to use later to come up with the identity’s age.

I use an array to hold each bit of the birth date, a formatted version of the birth date, and the age. To get the parts of the birth date I just call the format method on my $birthday DateTime object. To get the age I use the diff method on my $today DateTime object to find the number of full years between the $birthday object and the $today object.

If all you want is to pick a random date between two dates, then this is obviously more than you need. You could probably get away with this, if you were sure that the input dates were valid:

function randomDate($startDate = 'now', $endDate = 'now')
{
    $minDate = new DateTime($startDate);
    $maxDate = new DateTime($endDate);

    $minEpoch = $minDate->format('U');
    $maxEpoch = $maxDate->format('U');

    $randomDate = new DateTime('@'.mt_rand($minEpoch, $maxEpoch));
    $today = new DateTime('now');

    $date = array(
        'year'      => $randomDate->format('Y'),
        'month'     => $randomDate->format('n'),
        'month-name'=> $randomDate->format('F'),
        'day'       => $randomDate->format('j'),
        'formatted' => $randomDate->format('n/j/Y'),
        'age'       => $today->diff($randomDate)->y,
    );

    return $date;
}

DateTime can figure out most dates, so you could do something like randomDate(‘March 1, 1980’, ‘5/20/1999’). If you don’t pass any dates in then it will use today’s date.

I could probably re-use some of those DateTime objects, but I don’t think having a few of them matters enough to obfuscate the code. I could also just return the $birthday object rather than building an array to return, but my existing code expects an array.

I’m open to critiques and suggestions for improvements. I’m thick skinned so feel free to completely bash my code if you’d like. Or if you are having a problem with the code, please let me know and I’d be happy to try to help you out!

Read More

Fake Music Generator

Fake Music Generator

Fake-Music-GeneratorI had an awesome idea the other day: create my own computer generated music by using Markov chains and MIDI files. “I’m a genius! Nobody has ever thought of this before,” I said to myself. I registered FakeMusicGenerator.com and planned on looking into it later. Well turns out everybody has thought of this before. Oh well. I already had the domain so I went for it.

I ended up taking the lazy route and used some existing free code I found to generate music. I then converted to MP3 using a fancy SoundFont (I never knew that sounds had fonts but I guess it makes sense) so they would sound better than a MIDI.

I took it a step further and added fake track names, album titles, and artist names. I even added computer generated art for the album cover. I think it is shaping up pretty well.

Anyways, if you are bored and need some free instrumental music (maybe for a YouTube video or something), then feel free to check out the Fake Music Generator.

Read More

Starting a new company

Starting a new company

About two weeks ago I was happily drifting off to sleep when my cell phone rang. I hate getting voicemails more than I hate getting woken up in the middle of the night so I took the call. To my surprise it was a mission buddy, Jon, pitching me a business he wanted me to invest in. I’ve been trying to come up with a unique physical product to sell for years, so I was very excited to have one practically fall in my lap. I did some research, liked what I saw, and a few days later we signed an operating agreement for our new company. A few days after that (on my birthday!) we finalized a deal to purchase the rights to our new product from its creator.

So what, you may be asking, does this new company sell? SproutBoards. We aren’t a household name yet so you probably have no idea what that is, so let me explain. A SproutBoard is a sort of motherboard or breakout board for the popular Arduino microcontroller. This gives any SproutBoard user a slew of inputs that can be connected to sensors, outputs that can be used to control equipment, and the ability to use one of our acrylic chassis to mount their project on the wall or in a standard server rack. In addition to that we are developing software for the SproutBoard that will make it easy to tell your SproutBoard to take different actions based on the input of sensors.

sproutboard

So what can you actually make with a SproutBoard? Here are a few ideas:

  1. A server room monitor that can email, txt, or call you when the power goes out, it gets too hot, it detects flooding, or an unauthorized person tries to get to your equipment.
  2. An indoor greenhouse that turns the lights on and off as needed, maintains the appropriate humidity, and waters the plants when they get thirsty.
  3. A home security system that lets you use an RFID key fob to enter your home without a key, uses a low cost prepaid wireless service to alert you to problems even if the power is out, and lets you grant access to visitors even if you aren’t home.
  4. An aquarium control system that monitors temperature, pH, salt content, oxygen levels, and the water level, and lets you know the moment your fish are in danger.

Those are just a handful of the projects that we’ll be providing full tutorials and documentation for in the near future.

Well that is pretty much it for now. My partner and I have been working long nights trying to get the website moved over to our own server, getting documentation cleaned up, and doing all the tedious new business paperwork. We’ve been brainstorming new products and have a long (very long) list of products we plan on developing in the near future. It’ll be a few days before our inventory arrives so we’ve temporarily suspended sales but you’ll be able to place an order soon.

We are also gearing up for the Mini Maker Faire in Austin on May 5th. Unfortunately I won’t be able to attend, but my partner will be there showing off a hydroponics system built using a SproutBoard. If you live anywhere near Austin, please stop by the Maker Faire and say hi to Jon for me!

Read More

SYN flood mitigation

My Sunday morning was ruined by a notification email from a monitoring service letting me know that my server was down. Eek! I took the normal first steps: try to SSH to the box and remote reboot. Still offline.

I started a chat with my hosting company’s tech support. They said that it looked like a SYN flood was in progress, and that I’d need to handle it on my own. They pointed to a few resources that gave suggestions on what to do, and helped me figure out how to SSH into my box on the hosting company’s private network (because the SYN flood was only affecting public network access). Overall, excellent support from SoftLayer.

Most websites that talked about SYN floods recommended turning on SYN cookies and adding a few iptables rules. With further research (and experience) I discovered that neither of these options really work well. I wasn’t even entirely convinced that I was being flooded. I think all the traffic was legitimate (I run a lot of popular sites).

So what to do? I read a few sites and came up with a solution.

First you need to keep in mind that a lot of default networking settings were developed way back in the day when computers weren’t nearly as powerful as they are now. My server is more than capable of handling the traffic but the default settings were keeping connections from getting through. To fix this, I needed to tell my server that it could handle additional connections. In a shell I issued these commands:

echo 16384 > /proc/sys/net/ipv4/tcp_max_syn_backlog
echo 16384 > /proc/sys/net/core/somaxconn

Next I updated a config file, /etc/sysctl.conf, so these settings will still be there next time I reboot:

net.ipv4.tcp_max_syn_backlog = 16384
net.core.somaxconn = 16384

Last, I had to tell Apache to handle more connections as well by editing the httpd.conf file:

ListenBacklog 16384

I restarted Apache and BAM! My sites were accessible again! Yay! My load average is still around 0.05 to 0.10 most of the time. Lots of free memory. Fast page loads. Everything works great. All it took was giving my server a little self confidence by letting it know it was capable of handling the traffic it was getting.

Read More

BYU Speeches player with history

Update 10/26/2012: The history functionality needs some work. I’ve tweaked it to only log a play if you listen for at least 30 seconds (to avoid accidentally logging something that you didn’t actually listen to), and it won’t log additional plays after the first play until you’ve switched to a new talk (so you don’t end up with 15 “you started…” records in a row for the same talk). I made the talk title and author name link to the appropriate pages so you can easily find what you want to listen to. I’ve also added some social sharing stuff (per Joey’s request) in case you really like a talk and want to send it to a friend.

Original post:

The BYU Speeches website provides over a thousand devotionals and firesides in MP3 format. The speakers include people you’ve never heard of, and a bunch of big name people, too (like apostles and prophets). I used to download the PDFs of the talks and read them, but I thought I’d give listening a try.

I stumbled on a player written by a friend of mine, Joey Novak, but the pretty looking version of it wasn’t working on my computer and it was missing a few features I wanted. Rather than bug him to change it I decided I’d just make my own. I got to work on it this morning and have a pretty functional player ready for use. There are a lot of features I’d like to implement, but I’m not going to go too crazy devoting tons of time to this until I get a chance to use it and see what would actually be useful.

The first step in building this was scraping the data. I wrote a small script that looped through all the content at BYU Speeches and scraped the author(s), speech title, date it was given, and the URL to the MP3. I shoved all of this into a database. Next I searched for a free MP3 player script, and found the same one that Joey was using. Turns out everyone uses jPlayer. It didn’t take long to get a basic working player up with a random playlist selected from my database. A great perk of using jPlayer is that it looks great on a mobile phone. I spent a few more minutes and added in the ability to pull up a playlist for a specific speaker.

Where I spent most of my time was working on a history system. I’ve added the ability to log in using a Google account, and then I’ll (confidentially) track what you listen to. This needs some work and I’m not satisfied with how it looks, but the basic functionality is there. It will let you know when you started listening to an item, finished listening to an item, and (bonus!) won’t select stuff you’ve already listened to when you pull up a random playlist. This is what I was really wanting because it drives me nuts to get 15 minutes into a talk only to realize I’ve already heard it. With about 1,400 talks at around 30 minutes each, I doubt I’ll ever run out of stuff to listen to.

I think I’ll eventually modify it to only log that you started listening to something once you’ve been listening for 60 seconds or so. This gives you time to decide if you want to listen to it or not without falsely thinking you’ve actually listened to it. I’m also planning on adding a “resume” function, so it will remember what you were listening to last and how far in you got, so it can automatically pick up where you left off when you pull the page up.

I also need to write an RSS parser so I can update my database whenever they announce new content. I don’t expect this will take long, I just haven’t gotten around to doing it yet.

Anyways, click here to check out my BYU Speeches player.

Read More