Switching to Octopress
I've decided to switch over to Octopress for blogging. Follow me here: http://www.pote.to
2025 on Tumblr: Trends That Defined the Year

if i look back, i am lost
art blog(derogatory)
Today's Document

oozey mess

PR's Tumblrdome
Mike Driver
AnasAbdin
NASA
Not today Justin
Xuebing Du

⁂
will byers stan first human second
Keni
TVSTRANGERTHINGS
taylor price
dirt enthusiast

★
ojovivo

titsay

seen from Malaysia
seen from United States

seen from Saudi Arabia

seen from Malaysia

seen from United States

seen from United States

seen from Canada

seen from Algeria
seen from Bosnia & Herzegovina

seen from Germany
seen from Thailand

seen from Slovakia

seen from United States
seen from Brazil

seen from Germany

seen from Colombia
seen from United States

seen from United States
seen from United States

seen from Tunisia
@thestartupacademy-blog
Switching to Octopress
I've decided to switch over to Octopress for blogging. Follow me here: http://www.pote.to
Designing a theme for Shopify on Ubuntu Linux (non-OSX) and Sublime Text 2
So it turns out developing locally with [Vision](http://wiki.shopify.com/Vision) is unsupported, and the Shopify wiki unhelpfully suggests I use Textmate2 on my Ubuntu machine. Before trying to drop the tmbundle into my packages directory, I found a thread stating that it didn't work and that pointed me to [dwarburton](https://bitbucket.org/dwarburton/sublimeshopify/overview)'s ST2 port, which works pretty well. ## Working from Sublime Text 2 only Here are the steps to install and use in case you're wondering: 1. Make sure you have an API key for your store: https://your-store.myshopify.com/admin/api 2. If you use [Mercurial](http://mercurial.selenic.com/) for version control, just follow the steps outlined in the readme 3. If you don't, just download a zip or tarball and extract to `~/.config/sublime-text-2/Packages` 4. To config, start ST2 and press `Ctrl + Shift + P` to bring up your command palette 5. Type `shopify` and it should autocomplete to show you 4 commands 6. Start with `Shopify: Edit store information` and follow the steps outlined in the [wiki](http://wiki.shopify.com/Shopify_Textmate_Bundle) Now you can use `Shopify: Edit asset` to download, edit and re-upload a file automatically on save. ## Working from terminal If you want to grab all the files of an existing theme or you prefer working in the command line, I recommend using Shopify's own [shopify_theme gem](https://github.com/Shopify/shopify_theme). Very easy to use, just follow the instructions in the readme. ## Using Liquid Liquid looks and feels pretty much like erb, so if you're familiar with that you won't have any trouble using Liquid for templating on Shopify. Here's the [documentation](http://wiki.shopify.com/Intro_to_Shopify) and a handy [cheatsheet](http://cheat.markdunkley.com/). ## Apps If you want to develop an app for Shopify then check out their [API documentation](http://api.shopify.com/) and install the [shopify_api gem](https://github.com/shopify/shopify_api).
Making a backup of your installed software on Ubuntu 12.04
On my old laptop I still run Windows and Ubuntu (using wubi) as a dual boot, and occasionally I get the mysterious disappearing root.disk bug, which is fairly annoying. I keep backups of my root.disk file periodically but sometimes there are gaps of a few days in between backups, so a disappearing root.disk could potentially be a huge time waster if I had spent some time say installing a particularly tricky library. To scratch my own itch, I wrote this shell script to run as a daily [cron](https://help.ubuntu.com/community/CronHowto) job. It basically exports a list of all your installed software on Ubuntu which you can later use as a backup. Restoring your software just takes a few commands (or you can put this in a script):
$ sudo apt-key add Repo.keys $ sudo cp sources.list /etc/apt/sources.list $ sudo cp -r sources.list.d/* /etc/apt/sources.list.d/ $ dpkg --set-selections < installed-software.list $ apt-get update $ dselect
## Install the backup script as a daily cron job [View on Github](http://poteto.github.com/ubuntu_backup) - Clone the shell script from the following git repo:
$ git clone [email protected]:poteto/ubuntu_backup.git
- Navigate to the cloned directory:
$ cd ubuntu_backup
- Edit the script if you wish to modify the backup folder path (by default it is saved to ~/Dropbox/ubuntu_backup). st is an alias in [oh-my-zsh](https://github.com/robbyrussell/oh-my-zsh) for the brilliant [Sublime Text 2](http://www.sublimetext.com/2) text editor, change it if you use something else like vim.
$ st backup.sh
- If you want to test it out just run:
$ sh backup.sh
You should see something like this in your terminal prompt:
Running backup.sh... [ SUCCESS ] Created /home/poteto/Dropbox/ubuntu-backups/2012-08-22-0121 [ SUCCESS ] installed-software.list [ SUCCESS ] sources.list [ SUCCESS ] sources.list.d [ SUCCESS ] Repo.keys 5 successes and 0 failures Exiting backup.sh
- To create a daily cron job just copy the script into the /etc/cron.daily folder as sudo:
$ sudo cp backup.sh /etc/cron.daily/
And that's it. See the [Github page](http://poteto.github.com/ubuntu_backup) for more detailed usage.
Eating the cold porridge
'You must eat the cold porridge,' he told me once.
It's a Chinese expression. Cantonese, I guess, because although he carried an old-fashioned blue British passport and was happy to call himself an Englishman, he was born in Hong Kong and sometimes you could tell that all the important things he believed were formed long ago and far away. Like the importance of eating the cold porridge.
I stopped what I was doing and stared at him. What was he going on about now? 'Eat the cold porridge.'
The way he explained it, eating the cold porridge means working at something for so long that when you get home there is nothing left to eat but cold porridge.
That's how you get good at something, he told me. That's how you get good at anything. You eat the cold porridge.
You work at it when the others are playing. You work at it when the others are watching television. You work at it when the others are sleeping.
To become the master of something, you must eat the cold porridge.
- Tony Parsons
Choosing the freemium business model can be brilliant or deadly. The difference lies not only in the execution of the marketing, but also in the nature of your product and the design of your business. Can you coordinate all three to become sustainably freemium by making a profit?
Summary
...
Batch trace geolocation from many IPs
My co-founder recently wanted to check out geolocations from a large bunch of IP addresses, but there wasn't an easy way to batch process it. Here's a quick Python script to do just that:
#!/usr/bin/python # ip_lookup.py by poteto # # Batch processes many IP addresses, and traces their geolocation. # The script outputs a .txt file which is then renamed to .csv, and is usable in Excel # # Use: # 1. Save your user email and IP addresses in a .csv file # - Each email and IP should be in its own column in Excel (or separated by commas) # - Enter the name of your .csv file: # batch_filename = "batch.csv" # # 2. Go to http://www.ipinfodb.com/ip_location_api.php and get your own API key # 3. Update the config with your API key: # key = "f1158e2461279f5b9076fc8c3d1d4dc41304d6e2bdebe42943241cd26d4b6dbb" # # 4. Run the script: # $ python ip_lookup.py import xml.etree.ElementTree as ET import urllib import csv import os # Process batch csv into list 'current_batch' def get_batch(batch_filename): "Creates list containing all the email and IP addresses from your batch file" batch = csv.reader(open(batch_filename, 'rb'), delimiter=',') return [[email, ip_addr] for email, ip_addr in batch] def batch_process(batch): "Runs your batch file through the IPInfoDB API" try: i = 0 output = open('output.txt', 'a') for email, ip_addr in batch: response = urllib.urlopen(url + "&ip=" + str(ip_addr)).read() tree = ET.fromstring(response) output.write(str(email) + ",") for child in tree: if child.tag == 'ipAddress': output.write(str(child.text) + ",") if child.tag == 'countryCode': output.write(str(child.text) + ",") if child.tag == 'timeZone': output.write(str(child.text) + "\n") i += 1 print "\t" + str(i) + " of " + str(len(batch)) + ":\t" + str(email) output.close() os.rename('output.txt', 'output.csv') print "Complete." except KeyboardInterrupt: output.close() print "\nExiting..." base_url = "http://api.ipinfodb.com/v3/ip-city/?format=xml&key=" url = base_url + key current_batch = get_batch(batch_filename) print "Added " + str(len(current_batch)) + " entries from " + str(batch_filename) + "\n" print "Working... This may take a while, please be patient.\n" batch_process(current_batch)
Elegantly said. Too many put an emphasis on being the first to think of an idea. So much so that they never actually create anything, they just keep trying to think of an idea that doesn't yet exist. The most iconic companies in the world weren't the first, they were simply the best. MySpace beat Facebook to the punch, HP hit the market before Apple, PicPlz existed before Instagram.
It didn't matter. They succeeded because they did it right.
Extract specific filetypes from many .zip files
I recently downloaded all the Mailchimp guides, and found that the filetype I wanted (PDFs) were buried inside each .zip file. I've been doing Udacity lessons and learning programming for a while now, so I decided to put my new skills to use.
I whipped up this little Python utility script that extracts all files of a specific filetype from many .zip files. It's not very pretty, but it works. It also helpfully ignores fake files in the __MACOSX folder (why would you do this Apple?).
# extract_filetype_from_zip.py # A simple script to extract all files of a certain filetype from many .zip files # * Requires Python installed on local computer VERSION = 0.1 # # Place this file in the folder where you have all your zipfiles. # Example use: # - To extract only PDFs: extract_filetype('.pdf', '/folder') where folder (with the /) is the dir you store your zipfiles # - 2nd argument is optional (defaults to root) import zipfile import os # to do: target directory for extraction, zip file passwords def extract_filetype(filetype, directory='root'): "Extracts files of filetype from a directory of zipfiles." if directory == 'root': working_dir = './' else: working_dir = '.' + str(directory) + '/' zipfile_list = zipfile_array(working_dir) if zipfile_list: print "Working to extract " + str(filetype) + " files from " + str(directory) + "...\n" for zips in zipfile_list: print " Found " + str(len(zips)) + " .zip files \n" for f in zips: with zipfile.ZipFile(f) as curr_zip: for target_filepath in curr_zip.namelist(): if target_filepath.endswith(filetype) and not target_filepath.startswith('__MACOSX/'): print " Extracting... " + str(target_filepath) curr_zip.extract(target_filepath) print "\nExtraction complete." else: print "No .zip files to extract from " + str(directory) def zipfile_array(directory): # this needs fixing to avoid empty arrays "Creates array of zipfile paths and names in the directory." return [[os.path.join(root, name) for name in files if name.endswith('.zip')] for root, dirs, files in os.walk(directory)] extract_filetype('.pdf', '/zip') # print filename_array('./zip')
Can't install rmagick gem (Ubuntu 11.10)
Ruby 1.9.2, Rails 3.2.2
Problem:
Installing rmagick (2.13.1) with native extensions Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension. /home/poteto/.rvm/rubies/ruby-1.9.2-p290/bin/ruby extconf.rb checking for Ruby version >= 1.8.5... yes checking for gcc... yes checking for Magick-config... no Can't install RMagick 2.13.1. Can't find Magick-config in /home/poteto/.rvm/gems/ruby-1.9.2-p290/bin:/home/poteto/.rvm/gems/ruby-1.9.2-p290@global/bin:/home/poteto/.rvm/rubies/ruby-1.9.2-p290/bin:/home/poteto/.rvm/bin:/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games *** extconf.rb failed *** Could not create Makefile due to some reason, probably lack of necessary libraries and/or headers. Check the mkmf.log file for more details. You may need configuration options. Provided configuration options: --with-opt-dir --with-opt-include --without-opt-include=${opt-dir}/include --with-opt-lib --without-opt-lib=${opt-dir}/lib --with-make-prog --without-make-prog --srcdir=. --curdir --ruby=/home/poteto/.rvm/rubies/ruby-1.9.2-p290/bin/ruby Gem files will remain installed in /home/poteto/.rvm/gems/ruby-1.9.2-p290/gems/rmagick-2.13.1 for inspection. Results logged to /home/poteto/.rvm/gems/ruby-1.9.2-p290/gems/rmagick-2.13.1/ext/RMagick/gem_make.out An error occured while installing rmagick (2.13.1), and Bundler cannot continue. Make sure that `gem install rmagick -v '2.13.1'` succeeds before bundling.
Solution:
$ sudo apt-get install libmagickwand-dev imagemagick
While it's easy to daydream about your startup's eventual billion dollar IPO, the reality is a lot less happy. People are resistant to change and mostly happy with the status quo, so why should they give your new product a try? You'll really need to communicate your value proposition to get your potential users to see why your product helps solve their problem. And when all else fails, A/B test everything.
On the flip side, first-time entrepreneurs often fail to realize that when you build something new, no one will care. People won’t use your product, won’t tell people about it, and almost certainly won’t pay for it. (There are exceptions – but these are as rare as winning the lottery). This doesn’t mean you’ll fail. It means you need to be smarter and harder working, and surround yourself with extraordinary people.
Import Heroku production Postgres db to local development db
Useful if you want to import some production data into your local database so you can do proper testing. I assume you already have the pgbackups addon enabled on your Heroku app. If not, add it first.
$ heroku pgbackups --app yourapp #=> ID | Backup Time | Size | Database #=> -----+---------------------+-------+------------------------ #=> a226 | 2012/02/22 20:02.19 | 5.3KB | DATABASE_URL #=> a227 | 2012/02/23 20:02.19 | 5.3KB | DATABASE_URL #=> b251 | 2012/02/24 16:08.02 | 5.3KB | HEROKU_POSTGRESQL_BLACK #=> b252 | 2012/02/24 16:08.53 | 5.3KB | HEROKU_POSTGRESQL_PINK $ heroku pgbackups:url b004 #=> "http://s3.amazonaws.com/hkpgbackups/[email protected]/b004.dump?AWSAccessKeyId=ABCD1234&Expires=1289261668&Signature=3mMBeKISewgEUDT%2FL5mRz4EYS4M%3D" $ curl -o latest.dump `heroku pgbackups:url` $ pg_restore --verbose --clean --no-acl --no-owner -h localhost -U your_db_username -d your_database latest.dump # Change localhost, your_db_username and your_database as necessary
Heroku shared db down
If you see this, the Heroku shared database is down.
2012-05-19T16:12:55+00:00 app[web.2]: /app/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.2/lib/active_record/connection_adapters/postgresql_adapter.rb:1194:in `initialize': could not connect to server: Connection timed out (PG::Error) 2012-05-19T16:12:55+00:00 app[web.2]: Is the server running on host "ec2-23-23-203-225.compute-1.amazonaws.com" and accepting 2012-05-19T16:12:55+00:00 app[web.2]: TCP/IP connections on port 5432?
This happened to us on a Saturday, and submitting a ticket to the Heroku support team yielded this unhelpful email:
We've received your support request (#52462), but it is currently outside of our regular support hours. Support hours are from 6:00 AM to 6:00 PM PST Monday through Friday, excluding U.S. holidays. You can expect a response within 1 business day.
Luckily, I checked the #heroku hashtag on Twitter and found out about their IRC channel. @asenchi, who claims to not know what the definition of a holiday is, helpfully booted up a new server for those affected.
All is good now. It's probably time for us to look at the Crane or Kappa PG plans, which @asenchi told me was more reliable than the shared db.
Apple’s welcome letter for new hires.
Can the same be said of the company you’re building?
via @m
Over the last few years I have made and seen many spreadsheets with financial models, projections and valuations. The list below shows some suggestions and tips on how to make credible, easier to understand and maintain, conservative financial models. It is by no means a definitive list, so I will...
Everything should be made as simple as possible, but not simpler. - Einstein
Is simple always better? In the world of technology, that really depends on your target audience. If you make something like a to-do list or a social photo-sharing app, simplicity becomes a requirement for good UX. If you're creating a new IDE, then simplicity may not work so well in your favor.
What's important to note is that although simplicity is not always a good choice, clarity is essential to good UX, regardless of the technical knowledge your users possess.
Udacity
Udacity is an amazing resource if you're learning programming for the first time. Here's an incomplete poker hand validator written in Python. Hopefully I'll be able to finish the rest of it.
#!usr/bin/python import itertools import random mydeck = [r + s for r in '23456789TJQKA' for s in 'SHDC'] def deal(numhands, n=5, deck=mydeck): "Shuffles deck, returns list of 52 cards" random.shuffle(deck) return [deck[n * i:n * (i + 1)] for i in range(numhands)] def best_hand(hand, n): "From a 7-card hand, return the best n card hand." return max(itertools.combinations(hand, n), key=hand_rank) def wild_cards(color): "Returns a list of possible cards based on the color of the jokers." suits = 'SC' if color == '?B' else 'HD' deck = [r + s for r in '23456789TJQKA' for s in suits] deck.reverse() return deck redcards = wild_cards('?R') blackcards = wild_cards('?B') def best_wild_hand(hand): "Try all values for jokers in all 5-card selections." hands = set(best_hand(h, 5) for h in itertools.product(*map(replacements, hand))) return max(hands, key=hand_rank) def replacements(card): if card == '?B': return blackcards elif card == '?R': return redcards else: return [card] def test_best_wild_hand(): assert (sorted(best_wild_hand("6C 7C 8C 9C TC 5C ?B".split())) == ['7C', '8C', '9C', 'JC', 'TC']) assert (sorted(best_wild_hand("TD TC 5H 5C 7C ?R ?B".split())) == ['7C', 'TC', 'TD', 'TH', 'TS']) assert (sorted(best_wild_hand("JD TC TH 7C 7D 7S 7H".split())) == ['7C', '7D', '7H', '7S', 'JD']) return 'test_best_wild_hand passes' def hand_rank(hand): "Return a value indicating the ranking of a hand." ranks = card_ranks(hand) if straight(ranks) and flush(hand): return (8, max(ranks)) elif kind(4, ranks): return (7, kind(4, ranks), kind(1, ranks)) elif kind(3, ranks) and kind(2, ranks): return (6, kind(3, ranks), kind(2, ranks)) elif flush(hand): return (5, ranks) elif straight(ranks): return (4, max(ranks)) elif kind(3, ranks): return (3, kind(3, ranks), ranks) elif two_pair(ranks): return (2, two_pair(ranks), ranks) elif kind(2, ranks): return (1, kind(2, ranks), ranks) else: return (0, ranks) def card_ranks(hand): "Return a list of the ranks, sorted with higher first." ranks = ['--23456789TJQKA'.index(r) for r, s in hand] ranks.sort(reverse=True) return [5, 4, 3, 2, 1] if (ranks == [14, 5, 4, 3, 2]) else ranks def flush(hand): "Return True if all the cards have the same suit." suits = [s for r, s in hand] return len(set(suits)) == 1 def straight(ranks): """Return True if the ordered ranks form a 5-card straight.""" return (max(ranks) - min(ranks) == 4) and len(set(ranks)) == 5 def kind(n, ranks): """Return the first rank that this hand has exactly n-of-a-kind of. Return None if there is no n-of-a-kind in the hand.""" for r in ranks: if ranks.count(r) == n: return r return None def two_pair(ranks): """If there are two pair here, return the two ranks of the two pairs, else None.""" pair = kind(2, ranks) lowpair = kind(2, list(reversed(ranks))) if pair and lowpair != pair: return (pair, lowpair) else: return None # print 'Test 1 : ' + str(best_wild_hand("6C 7C 8C 9C TC 5C ?B".split())) # print 'Test 2 : ' + str(best_wild_hand("TD TC 5H 5C 7C ?R ?B".split())) # print 'Test 3 : ' + str(best_wild_hand("JD TC TH 7C 7D 7S 7H".split())) print test_best_wild_hand()