Thursday, April 27, 2017

New Ubuntu EC2 Instance things to remember

if you want to attach an existing EBS volume to the instance, make sure it's in the same Availability Zone (like "us-east-1c")

To SSH at all:
download that private key that they make you create as part of the setup console
ssh -i (thatkey.pem) -l ubuntu
(there's one user created at the start, called ubuntu)
sudo adduser dantasse
sudo vim /etc/group # and add dantasse to sudo group
sudo vim /etc/ssh/sshd_config # and set PasswordAuthentication to yes
sudo /etc/init.d/ssh reload
Now you should be able to SSH.

Attach the EBS volume on the AWS console. For example, attach it as /dev/sdf. Because it's Ubuntu, it will be attached as /dev/xvdf instead. (because reasons :P) You have to mount it still, though. Say you want to mount it at /data:
sudo mkdir /data
sudo mount /dev/xvdf /data

Friday, December 16, 2016

Some things I learned in that adventure that I always forget

dpkg --get-selections | grep -v deinstall 
shows all packages you have installed on Ubuntu

/etc/apt/sources.list is where your repositories (for debian/etc packages) are stored

lsb_release -a
shows what Ubuntu release you're running

Thursday, December 15, 2016

A hairy adventure in upgrading Ubuntu and Postgres

(Detailing this more as a historical artifact than anything else, but it might help someone.)

I had an EC2 Ubuntu 14.04 Trusty machine running PostgreSQL 9.3 and PostGIS 2.1 that I had to upgrade. For whatever reason.
sudo do-release-upgrade

but PostGIS was holding it back. I don't remember how I found this out but I think it was a simple google. So I did:
sudo apt-get remove postgresql-9.3-postgis-2.1 # turns out this was a mistake
sudo do-release-upgrade

ok, now I'm on Ubuntu 16.04 Xenial. Now to upgrade postgres:
sudo pg_upgradecluster 9.3 main /data/db/postgresql/9.5/main

ugh but wait, can't do it without PostGIS. hold on --
psql tweet
tweet=# select * from tweet_pgh limit 1;
(some kind of error because I don't have PostGIS installed anymore.)

Huh.
psql tweet
tweet=# ALTER EXTENSION postgis UPDATE TO "2.3.1";
(some kind of error about "no update path from 2.1.2 to 2.3.1")

Huh. I guess I have to 1. get PostGIS back, 2. give it an "update path" to 2.3.1.
Getting PostGIS back:
sudo apt-get install postgresql-9.3-postgis-2.1
(not found)

Huh. I flailed here for a while and then downloaded 2.1.2 from source, and did the old
./configure
make
make install
But of course it wasn't that easy! Got one compile error about json and using this old ticket, realized I had to go into liblwgeom/lwin_geojson.c and edit "#include " to "#include "
Got another compile error and had to like #IFDEF out references to AggState and WindowAggState in lwgeom_accum.c (as in this old ticket) and assume it'll work (sure did). Or rather, I was able to configure and make it, then I just symlinked it into the right place:
sudo ln -s /home/dantasse/postgis-2.1.2/postgis/postgis-2.1.so /usr/lib/postgresql/9.3/lib 

THEN, I could do this:
tweet=# select * from tweet_pgh limit 1;

but I couldn't do this:
tweet=# ALTER EXTENSION postgis UPDATE TO "2.3.1";
Still getting the "no update path" error, until I found this old post, and then:
cp /usr/share/postgresql/9.5/extension/postgis--2.1.2--2.3.1.sql /usr/share/postgresql/9.3/extension/

Finally:
sudo pg_upgradecluster 9.3 main /data/db/postgresql/9.5/main # (took about 8 hours, b/c I think it has to dump and restore the whole DB?)
and we're all good. Ubuntu 16.04, Postgres server and client 9.5, PostGIS 2.3.

Can't believe that worked.

Monday, September 26, 2016

Moving a PostgreSQL table from an Ubuntu machine to a RHEL machine

pg_dump -Fc --file=(/.../pg_dump_file) --table=(tablename) --dbname=(database name)
(sftp the pg_dump_file to the new machine)
(install postgres by doing all of the below side bonus)
pg_restore --clean --create --dbname=(database name) --jobs=3 (pg_dump_file)

the -Fc means "binary format, not text"

Side bonus: installing PostgreSQL 9.4 on RHEL 6 (Red Hat Enterprise Linux,) (tips from here)
sudo yum localinstall https://download.postgresql.org/pub/repos/yum/9.4/redhat/rhel-6-x86_64/pgdg-redhat94-9.4-3.noarch.rpm # figure out what rpm you need here; if you just yum install postgresql you'll get an old version
sudo yum install postgresql94 postgresql94-server
sudo service postgresql-9.4 initdb # one-time initialization
sudo service postgresql-9.4 start # make the postgres server go
sudo service postgresql-9.4 on # make the postgres server go every time I log on

Note: on RHEL 7, you have to do these 3 lines instead of those last 3:
sudo /usr/pgsql-9.4/bin/postgresql94-setup initdb
sudo systemctl start postgresql-9.4.service
systemctl enable postgresql-9.4.service # so it starts at boot

sudo yum install postgresql94-contrib # for hstore
sudo rpm -ivh http://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm # to get the right EPEL rpm for RHEL 6; it's like adding a new APT repository in Ubuntu I think
sudo yum install postgis2_94 # more help here if you need

sudo su - postgres
psql
CREATE USER (myusername) WITH SUPERUSER;
CREATE DATABASE (dbname);
(go to that new database)
CREATE EXTENSION hstore;
CREATE EXTENSION postgis;
(quit postgres, e.g. with ctrl-d)
(log out of being the postgres user; e.g. with ctrl-d again)

Double note: I have 2 cloud machines, one on RHEL 6 and one on RHEL 7. The RHEL 7 one killed me at the "install postgis" step; adding the EPEL rpm didn't make all the build errors go away. (when I poked around for other EPEL rpms, I eventually found one that fixed most of the build errors, but then gave me another error where it was missing libpoppler.so-46. I think I was the first person ever to have this error. In a turn of lunacy, I spent part of the day building GDAL from source. Surprisingly, it kinda worked. But didn't get me to PostGIS.) So, if you googled this and are looking for an answer, I'm afraid I can't help. Sorry!

Thursday, February 11, 2016

Useful PostgreSQL setup recipes

CREATE USER foo;
(or equivalently: CREATE ROLE foo WITH login;)
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO foo;
or
GRANT SELECT ON ALL TABLES IN SCHEMA public TO foo;

I think these are all pretty self explanatory. Then the new user is up and running!
If you want the user to have a password:
CREATE USER foo WITH PASSWORD 'bar';

To get rid of the user: 
REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM foo;
DROP ROLE foo;

Thursday, January 14, 2016

ogr2ogr

Dear Future Dan,
I know you are trying to convert a shapefile to a GeoJSON file. I know this because you googled "site:ilessendata.blogspot.com ogr2ogr". Here is what you want to do:

ogr2ogr -t_srs EPSG:4326 -f GeoJSON output.json input.shp

Love, Past Dan

Monday, November 9, 2015

Dump a PostgreSQL table to json

This seems like it should be easy but it isn't, and I've had to look it up a couple times. Here's the magic juice:

copy (select array_to_json(array_agg(row_to_json(t)))
    from (
      select * from tweet_pgh limit 5
    ) t
) to '/home/dantasse/data_dump/foo.json';

gets 5 rows from the tweet_pgh table and dumps them to that file. (the directory has to be world-writeable I think, because it'll execute as the postgres user.)

Edit:
COPY (SELECT ROW_TO_JSON(t) FROM (SELECT ST_AsGeoJSON(coordinates), * FROM tweet_austin) t) TO '/data/austin.json';
or just
COPY (SELECT ROW_TO_JSON(t) FROM (SELECT * FROM tweet_austin) t) TO '/data/austin.json';
seems to work better when you're memory limited.

Monday, May 4, 2015

"Let's Hadoop Something" on Amazon EMR

Edit: ugh, disregard this post maybe; still couldn't get the program to both run without errors and produce correct output. Started using Spark locally instead; it's better for my needs anyway. Leaving it up just in case this helps at all.

That's the goal I set out with: to run *something* on Hadoop. In this case it was simple: in each tweet, get the frequency of each emoji. So if a tweet is:
☃☃☃ it's cold ☃☃☃☹☹
I want to get "[6, 2]" because there's one emoji that happens 6x and one that is 2x.
Then I want to get the overall frequency of each frequency. Basically, I just want to know how many times people tweet an emoji once, and how many times they spam it 10x.

(the clever among you would recognize that this doesn't require any hadoops. indeed, it takes about 10 seconds to just loop through a 500mb text file with a bunch of tweets and spit out the answer. so in the real world, I should not be map-reducing anything here. but it's a learning experience; bear with me.)

Writing map-reduce code involves separating your code into mappers and reducers. For python, for example, this is easy: just make one python file for your mapper and one for your reducer, that both take in lines on sys.stdin and output lines to stdout. (this was unexpectedly simple for me. I like that they just use stdin and stdout, and one-data-point-per-line.)

Upload everything to s3: your mapper script, your reducer script, and all your input data (in the form of a text file).

Then create an EMR cluster (using all the defaults, but do add an SSH key and just take m1.medium instances if you're just testing, as they're the cheapest), and add a "step" that is a "streaming program". Terminate your cluster when you're done so you don't spend extra money.

You'll find your output in the output location that you chose, in the form of a few files like "part-00000", "part-00001". Not sure why it doesn't keep reducing until you're down to one final output, but I guess that is for you to do on your own?

Otherwise, good luck! This took me about a couple bucks to run, plus $15ish in debugging. It is frustrating when debugging costs real money. So it goes.

More caveats!
- as of May 5, 2015, Amazon EMR only supports Python 2.6.9. This is mildly frustrating. (here is a list of what it supports that will hopefully be kept up to date) Test your code on python2.6 before you upload it; it'll save you some headaches.
- make sure you set the "output location" to be a folder that doesn't exist yet. Otherwise it will just crash. You can figure this out from the logs, but each map-reduce job is ~20 min and probably a buck or two so better not to waste any.
- sometimes the logs are hard to find. The most valuable ones will be available through the EC2 console. You can find them here:
Click "view logs" to see logs related to the whole job, or go into "view jobs" then "view tasks" then "view attempts" to see logs for each individual mapper or reducer. Sometimes the logs will not show up. This is frustrating. Wait a few minutes and try again. If they're still not there, then I am not sure why. Sorry about it.

Friday, April 10, 2015

MongoDB vs PostgreSQL for geo queries: postgres is indeed pretty much across-the-board faster

ok, I have some benchmark numbers. Two almost-identical databases, one in mongo, one in postgres, so I can actually compare apples to apples. In Mongo I have everything in one collection. In postgres, I have two tables: tweet_pgh, which is all the data, and tweet_pgh_small, which is just the data I need.

These tables are pretty optimized: the mongo collection has indices on user.screen name, coordinates (geospatial), coordinates.coordinates.0 and coordinates.coordinates.1. The sql tables have btree indices on user_screen_name and gist indices on the coordinates (clustered). Oh, and of course I've got PostGIS installed.

Every query here selects everything in a table, then iterates through it (just incrementing a counter) to simulate a semi-realistic use case.

Count in mongo: 3653683
Count in postgres: 3653278

Searching for single user stuff. User A has 2942 items, User B has 1928 items, User C has 3499 items. (using A, B, and C here for their anonymity instead of their real twitter handles.) First three are mongo, the rest are postgres.
Mongo: {'user.screen_name': '(user A)'} 11 sec
Mongo: {'user.screen_name': '(user B)'} 6 sec
Mongo: {'user.screen_name': '(user C)'} 24 sec
Postgres: SELECT * FROM tweet_pgh WHERE user_screen_name = '(user A)'; 5 sec
Postgres: SELECT * FROM tweet_pgh WHERE user_screen_name = '(user B)'; 6 sec
Postgres: SELECT * FROM tweet_pgh WHERE user_screen_name = '(user C)'; 6 sec
Postgres: SELECT * FROM tweet_pgh_small WHERE user_screen_name = '(user A)'; 1.9 sec
Postgres: SELECT * FROM tweet_pgh_small WHERE user_screen_name = '(user B)'; 1.4 sec
Postgres: SELECT * FROM tweet_pgh_small WHERE user_screen_name = '(user C)'; 1.9 sec

Huh. So for these it looks like postgres takes either the same amount of time, half the time, or 1/4 the time. But the real win comes from just having a smaller table.

Searching for geographic areas. And these are *with geospatial indices* on both tables. Here I'm searching for a 0.01-degree lat by 0.01-degree lng box, which has 69k things in it.

Mongo: {'coordinates': {'$geoWithin': {'$geometry': {'type': 'Polygon', 'coordinates': [[[-79.99, 40.44], [-79.99, 40.45], [-80, 40.45], [-80, 40.44], [-79.99, 40.44]]]}}}} - 53 min
Postgres: SELECT * FROM tweet_pgh WHERE ST_MakeEnvelope(-80, 40.44, -79.99, 40.45, 4326) ~ coordinates; 33 sec
Postgres: SELECT * FROM tweet_pgh_small WHERE ST_MakeEnvelope(-80, 40.44, -79.99, 40.45, 4326) ~ coordinates; 3 sec

Then try the same thing with a smaller box (0.001x0.001 degree) with way fewer things (~ 5):
Mongo: {'coordinates': {'$geoWithin': {'$geometry': {'type': 'Polygon', 'coordinates': [[[-79.899, 40.44], [-79.899, 40.441], [-79.9, 40.441], [-79.9, 40.44], [-79.899, 40.44]]]}}}}; 7 min
Postgres: SELECT * FROM tweet_pgh WHERE ST_MakeEnvelope(-79.9, 40.44, -79.899, 40.441, 4326) ~ coordinates; 0.16 sec
Postgres: SELECT * FROM tweet_pgh_small WHERE ST_MakeEnvelope(-79.9, 40.44, -79.899, 40.441, 4326) ~ coordinates; 0.07 sec
(still waiting on the mongo query for this)

Wow! So, wait. So the same query that took 53 minutes in Mongo took 33 seconds in postgres. And three seconds in a reduced-size table.

It's a little unfair; in Mongo you can do a little better by using $gt and $lt on the coordinates instead of doing a $geoWithin - it's easier to compare numbers than coordinates, if you're just doing a box query. So we have the following:

{'coordinates.coordinates.1': {'$gt': 40.45, '$lt': 40.46}, 'coordinates.coordinates.0': {'$gt': -79.95, '$lt': -79.94}} 20 min
{'coordinates.coordinates.1': {'$gt': 40.46, '$lt': 40.461}, 'coordinates.coordinates.0': {'$gt': -79.97, '$lt': -79.969}} 2 min

But this is still nowhere near the postgres time! So:

- use PostgreSQL with PostGIS for geo queries, not MongoDB
- this is especially true if you can index and cluster your dbs
- maybe postgres is a little better for simpler queries too
- in postgres (maybe in mongo too) there is a lot of benefit to be had from cutting down the size of your tables as small as possible

Saturday, January 24, 2015

Dealing with images on the Pebble watch

My goal: a Pokemon watch, that shows a pokemon each minute instead of the numerical minute. I figured I'd get 144x144 images, and then just display one each minute. The challenges:

1. You need black and white (not grayscale) images. This is easily solved with a tool like HyperDither, which uses "dithering" to create shades of gray using only black and white pixels.
For example, changes this color Bulbasaur into this black and white one:


This is nice. HyperDither has a batch mode, which works, but it actually makes slightly smaller PNGs if you do it one at a time.

2. Saving space. You get 24kb RAM per app and 96kb storage for images/fonts per app. It's not easy to fit all the images you need in 96kb. I wanted 60 images, so I had to average ~1.5kb per image. Luckily, black/white 144x144 PNGs are not so big. Using HyperDither, I got pretty close to 96kb, but not quite: I'd have 90kb of PNGs locally, but when I uploaded them to the Pebble, it converted them to "pbi" files, which are almost double the size.

(also note that if you include too many pictures that are over 96kb, you'll get a cryptic error: it'll compile fine, but then will say "Installation failed. Check your phone for details." but no real way to check your phone for details, and no hints about why it fails. You'll get a similar cryptic error if you upload an image but never reference it in your code (stack overflow related question).

Spriting
So I figured there was some overhead per-image, so I tried spriting, or combining multiple images into one big image, like so: (created with InstantSprite, which is awesome and free)


and then you just take a subset of that image at a time. Pebble provides a call, gbitmap_create_as_sub_bitmap, to do exactly that. However, a few problems:
- if you make an image that is too big (like 10 pokemon at a time, or 1440x144), then gbitmap_create_as_sub_bitmap just crashes the app with no feedback. I found that 6 pokemon (864x144) worked, while 1440x144 didn't. Not sure what the actual limit is.
- it doesn't even make the images smaller! 10 PNGs with 6 pokemon each is not really any smaller than 60 PNGs with 1 pokemon each.

uPNG. Forget Spriting.
So, forget spriting. I then found Matthew Hungerford's port of uPNG, which is a library that lets you use raw PNG files instead of converting them to PBI first. Just include the .c and .h files, use gbitmap_create_with_png_resource instead of gbitmap_create_with_resource, and then edit your appinfo.json file to change the type of each image from "png" to "raw".
(editing your appinfo.json may require pushing your code from CloudPebble to Github, then cloning it locally, editing the file, committing and pushing your change. also, you might have to move your image files from resources/images/foo.png to resources/data/foo.png. anyway, this is nice, because you can then upload a bunch of images by editing a text file instead of uploading through the GUI a lot.)

He also provides scripts to transform images to B/W or dithered first, which ended up saving a few kb in my case.

In the end, thanks to uPNG, I had 80kb of images that actually stayed 80kb. (plus a little bit of overhead per image, but didn't matter.) Success! Here's my watch code on Github.

Other Tips
- Use logging, like: APP_LOG(APP_LOG_LEVEL_DEBUG, "hello"); - this took me too long to discover.

Sunday, October 19, 2014

Starting MongoDB on OS X

When you install MongoDB on a Mac with Homebrew, it spits out this important info:

To have launchd start mongodb at login:
    ln -sfv /usr/local/opt/mongodb/*.plist ~/Library/LaunchAgents
Then to load mongodb now:
    launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mongodb.plist
Or, if you don't want/need launchctl, you can just run:
    mongod --config /usr/local/etc/mongod.conf

This is important because the MongoDB docs tell you to run it by "sudo service mongodb start", which doesn't work on macs. Macs use "launchctl" instead of "service" (and they work differently). The things that they launch are defined by plist files in ~/Library/LaunchAgents.

There used to be a thing called "brew services", but now if you use that, it will tell you that it's unsupported and will be removed soon, so I guess don't use it.

Or if you just want to start it for a little while now, you could just start it with "mongod", but that doesn't work because you have to tell it where the config file is, which tells mongo where the database should be. Of course, you might not know where the config file or the database are. That's what the last line (mongod --config ...) is for.

Another tip: MongoDB logs are stored by default in:
/var/log/mongodb/mongodb.log

Sunday, February 23, 2014

Making maps in python, step 1: installing dependencies

I want to plot some things on a map. I got one guide from here, that uses Basemap.

Basemap:
brew install geos
brew install gdal
brew install gfortran

pip install matplotlib
pip install numpy
pip install pandas
pip install shapely
pip install basemap --allow-external basemap --allow-unverified basemap
pip install scipy
pip install pysal
pip install Fiona
pip install descartes

Okay, geez, I had written a ton more in this Blogger window, it looked like it was saving my draft, but then it... somehow didn't? Jesus, nothing on computers ever works. All right, I'll try to recreate it:

on Ubuntu, I wanted to pip install as much as possible (because of virtualenv) so I did stuff like this:
sudo apt-get build-dep numpy scipy matplotlib
pip install numpy scipy matplotlib
I think I just used pip for pandas, shapely, pysal, descartes. Fiona required something else, like apt-getting gdal or something?

Kartograph:
Ubuntu: http://kartograph.org/docs/kartograph.py/install-ubuntu.html
OSX: pip install -r https://raw.github.com/kartograph/kartograph.py/master/requirements.txt worked, I think because I already had installed gdal via brew.

Cartopy:
Ubuntu: https://github.com/SciTools/cartopy/issues/46
OSX: https://github.com/SciTools/cartopy/issues/48

Monday, February 3, 2014

Python sentence segmentation, kind of quick and mostly legit

Sentence segmentation (splitting a big block of text into sentences) is not trivial. You can't just split on periods, for example, because you'll get tripped up on every Dr. and Ms. and etc. and so on! However, it's mostly solved and in libraries, so here's a quick way to do it in python.

NLTK is a pretty general-purpose natural language processing toolkit. You could install the whole thing via instructions on their website. But that will also install a lot of other NLP tools. Also, a lot of these tools can be trained, which makes them more accurate if you have training data, but more difficult to get started if you don't have such training data. To get a pre-trained model:

- download Punkt from NLTK Data (direct link to Punkt)
- unzip it and copy english.pickle into the same directory as your python file. This is the trained model, which has been serialized out to a file. (obviously, this assumes you're segmenting English text; if not, grab one of the other .pickle files.)
- in your python code, unpickle it like so:
import pickle
segmenter_file = open('english.pickle', 'r')
sentence_segmenter = pickle.Unpickler(segmenter_file).load()
- then call:
sentences = sentence_segmenter.tokenize(text)
(where "text" is a string containing all your text).

Sunday, December 1, 2013

Sony Smartwatch 2 Programming

Some tips:
- the documentation seems to be, well, sparse. The most helpful thing I've found was code samples. They released five with the SDK (Sample{Control, Widget, AdvancedControl, Sensor, Notification}Extension) and two more here (EightPuzzleExtension and OSS_MusicExtension). Load them up into Eclipse as Android projects, they mostly work out of the box.
- well, mostly. For EightPuzzleExtension, I had to add <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> to the Android Manifest.
- also useful was Xiang "Anthony" Chen's HelloWatch post. All the example apps have 4 classes, and Anthony explains them better than I can. Almost all of my code went in the Extension app.

Here's the app I've been working on: MorseWatch. Mostly just takes in touch events. Also, I made a HelloSmartwatch app that uses accelerometers.

Maybe more details later. In the meantime, better to get some info and helpful links out there as soon as possible.

Excursions in rooting my Nexus 4

Man, I never really wanted to dig into phone rooting, but I recently went to do a Portable Wi-fi Hotspot and found that T-Mobile now blocks all data when I tether through my phone. (which is insane. why does T-Mobile care whether I'm using my phone or computer? I'm not using a ton of data. but anyway.)

I found this post, which explains how to edit one sqlite database to allow tethering again.

Unfortunately, it's in /data/data/..., which is only editable (or even viewable) if you have root access. (this is I think the same as root on mac/linux, which is roughly the ability to "sudo".) For some reason, Android makes this difficult.

Luckily, xda-developers to the rescue.
Nexus 4 Rooting Guide.
Backing up your phone before you root it.

How'd it go? Pretty smoothly. Wiped my phone, but the two-part backup in the link above (both the "adb pull" and the "adb backup" part) meant I didn't lose much. Had to reconfigure a couple minor things, not bad. And now I can tether!

The major steps, in my mind, are:
- back up your stuff
- unlock the bootloader (this is when your phone gets wiped)
- flash (install) the custom recovery
- install the SU app or something? a little unclear on this step, but it involves installing SuperSU.
- restore your data

A couple of gotchas:
- as usual when reinstalling everything on your phone, if you use two-factor authentication (as you should), generating some backup codes beforehand makes everything easier.
- on the "clockwork mod" site, all you need is the one in the "download recovery" column.
- don't download/install over-the-air Android updates. It breaks your root. (which is not a huge deal, but it means you have to re-flash the recovery and re-install the SU app.)

Friday, August 16, 2013

find | xargs rm, if your filenames have spaces

I have a nested file structure, with 200 subdirectories, and I want to get rid of everything that ends with "Deriv.csv" (but keep all other files). Usually I would do this:
find . -name "*Deriv.csv"
to find all the files (find everything in the directory structure starting here (.) that has the name "anything that ends with Deriv.csv"), and then just rm them all, with the help of xargs. It would look like this:
find . -name "*Deriv.csv" | xargs rm
(this is a good pattern to know, though a little hard to wrap your head around at first; piping to xargs makes the results of your first thing be the argument to the second thing. So if it finds file1Deriv.csv, file2Deriv.csv, file3Deriv.csv, then the | xargs rm makes it do "rm file1Deriv.csv file2Deriv.csv file3Deriv.csv".)

The added difficulty comes in because some of the filenames have spaces, so the find returns something like:
file 1 Deriv.csv
file 2 Deriv.csv
and then just passing that to xargs rm makes it run:
rm file 1 Deriv.csv file 2 Deriv.csv
which of course makes it complain that "file" is not found, "1" is not found, "Deriv.csv" is not found, etc. (I'm lucky that I didn't have any stray things called "file" or "Deriv.csv" sitting around that I wanted to keep, or they would have been removed by this mistake!)

One thing I found here (thanks!) is:
find . -name "*Deriv.csv" | xargs -I{} rm {}
-I does two things:
1. separate arguments by line, not by whitespace (great!)
2. make everything after the -I be its own command that you can control however you want. In this case, we do something simple: take the argument (with the {}) and put it after an rm. But you could also do, for example:
mkdir deriv_files
find . -name "*Deriv.csv" | xargs -I{} mv {} deriv_files/
which is pretty neat. (reference)

Monday, June 24, 2013

Twitter -> LCD via python and arduino

I've got a little LCD screen. I thought it'd be neat if anyone could tweet and it'd show up on that screen. Kind of like that goofy thing at basketball games where you can send a text message somewhere and maybe it'll show up on the huge scoreboard.

Code's on github, if you're into that sort of thing.

Getting the tweets from twitter onto my computer

I created a new account called @dansoffice, and I want to get every tweet that mentions @dansoffice into my twitter program. I want it real-time, and I don't want to get rate limited for polling a lot, so I looked to their streaming APIs.

What I want is the "user stream", so I have to authenticate as @dansoffice and send a GET to: https://userstream.twitter.com/1.1/user.json

Requests is a nice python library to make http easy. Pair it with requests_oauthlib (installed via pip; don't confuse it with requests_oauth), and the authentication is easy too. (make a Twitter app, go to the "OAuth tool", and it will tell you your access token and secret, and your consumer key and secret; use requests_oauthlib per their documentation. "consumer" = "client" in this case.)

And Requests even handles streaming requests via iter_lines... sort of. It's got a "chunk size" - a buffer that must fill up before it does anything. For some reason, this sometimes stops tweets from being read, until the next tweet comes in and clears out the buffer. Known issue.

So in my case, a terrible hack: also request that tweets are delimited by length, so the length comes in, followed by a newline, and clears the buffer; at this point, send another request (this one non-streaming, to mentions_timeline) to get the actual text of the most recent @dansoffice mention. Ugh.

Getting the tweets from my computer to the LCD

Python writes nicely to serial, via pyserial. Found my serial port's name from Arduino (tools->serial port menu). Easy enough to write to it when I get a tweet.

The LCD screen I have is this 20x4 display. I had to solder some header pins to it. Arduino has a nice LCD demo, which was easy and straightforward after I fixed my shoddy solder job. At that point, it became just a matter of chopping a string into 4 lines. (Nothing is easy in C.)

Fin

Now, as long as my little twitter monitor is running, and the Arduino is connected, tweets mentioning @dansoffice show up in real time. Now all I have to do is get a long mini-USB cable to stretch to my window. All the tweet-publicizing excitement of the NBA!

(maybe my next thing should mimic the Skinner-style pizza/t-shirt bribes and exhortations to MAKE SOME NOISE.)

Wednesday, June 19, 2013

Embedding a browser in a python application on a mac

I heard about PyQtWebkit (e.g. here). Sounds great.

First I had to get PyQt. Or maybe PyQt and Webkit, or Py and QtWebkit, or who knows. I wanted all those parts.

Tried to install it from source (e.g. from Riverbank) but that was a hopeless mess. Version 4 or 5? I don't know. I had to build SIP first, which eventually went fine. I tried to build PyQt, but had trouble because it couldn't find qmake, so I went looking for that. But then (as I often do when building things from source) I wondered if I was doing this whole thing wrong.

Tried to install PyQt using MacPorts, but either didn't find the right port or something didn't work. So I tried Homebrew (which is like MacPorts but newer; says you shouldn't run both but it's been okay for me so far):
brew install pyqt

Turns out, I guess, PyQtWebkit is included in PyQt; I just ran the code at the top of the above link, and bam! Google.pl in a python Qt window.
I wonder if I could have used pip to get PyQt; looks doubtful though.

Looks like instead maybe I could have downloaded PyQtX. But I haven't tried that.

Related: Selenium, but that drives an external browser, doesn't let you put one in your own app. Followed the instructions here.

Monday, November 12, 2012

Android accelerometer sampling rates

An app I'm working on relies on quick spikes in accelerometer activity, like fractions of a second, so I have to have a pretty fast accelerometer. Android lets you specify accelerometer sampling rate, kind of: you can choose SENSOR_DELAY_NORMAL, SENSOR_DELAY_UI, SENSOR_DELAY_GAME, or SENSOR_DELAY_FASTEST. Then the phone will try to give you samples at a rate that works for your app.

On NORMAL, my Nexus S gives me about 7Hz. Not at all fast enough. On UI, GAME, or FASTEST, I get about 49Hz, which is almost fast enough. So I gave some friends a little test app which determined their phone's FASTEST sample rate and here's what I found (readings in Hz):

Motorola Droid X: 5.19
Motorola Droid X2: 6.23
ZTE Blade: 9.11
HTC Nexus One: 24.86
HTC Evo 4G: 37.54
HTC Wildfire: 38.40*
HTC Desire: 47.27*
LG Nexus S: 49.44
Samsung Galaxy S3: 93.98
Motorola Atrix: 94.32
Sony Ericsson x10 Mini: 94.77**
Samsung Galaxy S2: 96.03, 97.5
Samsung Galaxy Nexus: 99.8
Samsung Note II: 100.0
Samsung Galaxy S3: 100
Samsung Galaxy Teos: 109.42*
LG Nexus 4: 198

(* from this stack overflow post)
(** from this slideshow, slide 13)

Want to add a reading? Here's how:
1. Enable installation of apps from unknown sources (Settings -> Security -> Unknown Sources)
2. Download this app
3. Tap "start", wait 5 seconds, and post (in a comment) which phone you have and what reading you get.
Edit: thanks all for adding to this! If you're looking for some other phones, check out the comments below.

Sunday, May 20, 2012

vim: using the system clipboard

It annoyed me that I couldn't yank (copy) something in vim using y and then paste it in something else with ctrl-v, or vice versa. I found that the following line (linux, x11, vim 7.3), makes it just work:

:set clipboard=unnamedplus

What's going on here?
vim has "registers." Each register is like its own clipboard. You can choose your register with ". So for example:
"ayy
means "using the a register ("a), copy/yank the current line (yy)". But most of the registers only live within vim. Vim has two special registers though: * and +. On Windows and Mac, they are both the system clipboard. On X11 they are different and only the + is the system clipboard. So in vim on either system, you should be able to type:
"+yy
to copy the current line into the system clipboard, and then ctrl-v in a windowed application to paste it. Then the "set clipboard=unnamedplus" line just makes the + register the default one.

More info from vim tips here and here.

Side note: I think that by saying "system clipboard" I am being a little vague because there are actually three system clipboards in X11, but the ctrl-c ctrl-v one is the one I always want. I think this is called, amusingly, the CLIPBOARD clipboard. More here.

Another side note: on my older Macbook (OS X 10.6.8 Snow Leopard), I couldn't easily get Vim 7.3.74+ (where clipboard=unnamedplus is introduced). The MacVim version available is 7.3.53. However, as mentioned above, * and + are both the system clipboard on a Mac, so set clipboard=unnamed works. (be careful if using tmux.) You can check if unnamedplus is available, so my .vimrc now looks like:
if has('unnamedplus')

  set clipboard=unnamedplus
else
  set clipboard=unnamed
endif