Vinod Kurup

Hospitalist/programmer in search of the meaning of life

Sep 22, 2018 - 2 minute read - Comments - family garden

Starting a garden

I’m going to be a gardener. Yup, just decided. Well, re-decided, I guess, since we already have a garden. It’s a strip of dirt on the side of our house and we’ve been trying to grow vegetables the past couple years. But it has some problems. It’s way out of the way, so it’s a pain to check on it (since I’m lazy). The ground just underneath the topsoil is hard clay. It’s shaded most of the day either by our house, or by the beautiful forest surrounding our house. And I haven’t figured out a way to keep the deer from eating the few vegetables that we are able to grow in there. And even despite all those problems we’ve been able to grow some zucchini, okra, tomatoes and basil, which has been super exciting, and makes us want more! Here’s a photo of the harvest that greeted us when we returned from a week-long trip to visit my parents in Dallas this summer:

August Harvest August Harvest

But, in general, things end up getting eaten by the deer or overrun by weeds, so we’re trying a new approach now, based on the book, Square Foot Gardening. Kavi and Anika helped me build this raised bed, which is itself a huge accomplishment given how not-handy I am.

Our Little Garden Our Little Garden

This was my first time using a drill since shop class in high school! We’re going to try using a fishing line fence to keep the deer away. I’m not very optimistic about that, but fingers-crossed! We can also see the garden from our living room, so maybe I can just shoo them away any time they saunter by. The garden is looking quite pathetic because we bought some seedlings from the Carrboro Farmer’s Market but then all our garden plans got sidetracked by Hurricane Florence (which fortunately treated us very kindly, all things considered). So, the seedlings wilted away in our screen porch before we finally got a chance to plant them this week. We’re hoping there’s still some life left in them. Anyway, our expectations are low, especially given how late in the summer we’re starting, but it would be nice to get one head of lettuce or something to grow. I’ll be sure to let you know!

May 31, 2018 - 2 minute read - Comments - india education

History of Indian Civilization

I briefly mentioned finding the 2012 UCLA lecture series, “History of Indian Civilization”, a couple days ago. I’m about halfway through and have found it thorougly illuminating. I learned a lot about India growing up, but never in a formal way, so it’s nice to have someone as skilled as Vinay Lal connect all the loose threads into a coherent history. One of the things that surprises me is that aside from the fact that classes are being streamed over the internet, there is nothing different than the lectures I skipped (I mean religiously attended!) about 20 years ago. It’s a large auditorium of students watching a very charismatic, knowledgeable speaker pacing at the front of the class, occasionally making mostly-legible markings on a chalkboard. Not that there’s anything wrong with that. I learned a lot back in college and I’m enjoying these lectures immensely. It just seems strange that so little has changed in the university clasroom (at least this one from 2012) when so much has changed in the world at large.

Actually, I find it reassuring that little has changed, at least in some classes. There was a time when the ability to download the audio of any university class was absolutely new. For me, that was around 2002 or so. I listened to CS61A (UC Berkley’s intro to CS course) and a few other similar courses back then. The limitations of internet bandwidth and storage made anything more than audio difficult back then. But now with those barriers crushed, I was afraid that everything would be “multimedia”, and not amenable to listening while I commute to work. So, it’s reassuring to find that audio-only dissemination of knowledge is still a thing.

May 30, 2018 - 2 minute read - Comments - programming postgresql

Autovacuum not running

OK, this is a debug session in progress, so don’t expect a nice solution at the end. We’re working on a project that does analysis of some public voter registration data. The DB is hosted on Amazon RDS and I’ve been perplexed by how poorly queries are performing there, despite the tables only have about 10 million rows. Simple queries are taking many minutes, which is orders of magnitude slower than my laptop.

Mark suggested running ‘VACUUM ANALYZE’, which I didn’t think would help because my understanding was that the autovacuum process in PostgreSQL would be taking care of that on a regular basis. These queries had been slow for days with no recent inserts or updates, so certainly autovacuum should have caught up to them by now. But, I tried it anyway and lo and behold:

db=> select count(*) from voter_ncvoter;
(1 row)
Time: 315777.051 ms
db=> vacuum analyze;
Time: 11377035.096 ms
db=> select count(*) from voter_ncvoter;
(1 row)
Time: 4300.107 ms

Woah, that worked! Sure, it took 3+ hours to run ANALYZE, but wow. So, why isn’t autovacuum automatically doing this for us. (I mean it has the phrase ‘auto’ in its name!!!)

I’ve found this great article on autovacuum basics which led me to do this query:

db=> select relname, n_live_tup, last_autoanalyze from pg_stat_all_tables where relname like 'voter_%';
       relname       | n_live_tup |       last_autoanalyze
 voter_changetracker |  306689271 | 2018-05-05 04:59:08.503876+00
 voter_filetracker   |         41 | 2018-05-13 02:00:47.802633+00
 voter_ncvhis        |          0 |
 voter_ncvoter       |   12336616 | 2018-05-06 13:20:30.073426+00
 voter_badlinerange  |        404 | 2018-04-10 05:44:39.949193+00
(5 rows)

So those 2 large tables haven’t been ANALYZEd in weeks, despite the fact that we import a 10 million row CSV once every week. This is the end of my debugging road, for now. Hopefully, I’ll figure out what’s going on.

May 29, 2018 - 2 minute read - Comments -

Renaissance Man

I love this post by Austin Kleon. As someone who has found very few things that I don’t find intriguing in some way, it is nice to know that there are others like me and that maybe there’s nothing wrong with that. I love being a doctor and I love being a programmer, and I love honing both of those crafts.

He also links to a 2012 New York Times article about a UCLA sophomore who spends 1 hour each day learning something new, mainly by taking online courses from other universities or watching how-to videos on YouTube. We live in a golden age, don’t we? I mean, sure, people will look back at our period of history and cringe quite a bit, but the idea that I can search just about any term, and find information about it on Wikipedia, and probably find a course on it by an actual college professor…. it’s just mind boggling. I’m lucky as it is, but I often think about if I was a kid growing up in this era. I’d be overwhelmed, sure, but I would totally be taking online courses left and right.

Actually, even if I weren’t a kid. I just googled the subject of that NYT article and found his twitter account. His most recent post is from 2016 but links to a YouTube series from UCLA on the history of India. I am TOTALLY watching that.

Jul 28, 2014 - 1 minute read - Comments - django programming

Using dynamic queries in a CBV

Let’s play ‘Spot the bug’. We’re building a simple system that shows photos. Each photo has a publish_date and we should only show photos that have been published (i.e. their publish_date is in the past).

class PhotoManager(models.Manager):
    def live(self, as_of=None):
        if as_of is None:
            as_of =
        return super().get_query_set().filter(publish_date__lte=as_of)

And the view to show those photos:

class ShowPhotosView(ListView):
    queryset =

Can you spot the bug? I sure didn’t… until the client complained that newly published photos never showed up on the site. Restarting the server fixed the problem temporarily. The newly published photos would show up, but then any photos published after the server restart again failed to display.

The problem is that the ShowPhotosView class is instantiated when the server starts. ShowPhotosView.queryset gets set to the value returned by That, in turn, is a QuerySet, but it’s a QuerySet with as_of set to WHEN THE SERVER STARTS UP. That as_of value never gets updated, so newer photos never get captured in the query.

There’s probably multiple ways to fix this, but an easy one is:

class ShowPhotosView(ListView):
    def get_queryset(self):

Now, instead of the queryset being instantiated at server start-up, it’s instantiated only when ShowPhotosView.get_queryset() is called, which is when a request is made.

Jul 23, 2014 - 2 minute read - Comments - programming emacs

Some Emacs Posts

A few cool Emacs posts have flown across my radar, so I’m noting them here for that time in the future when I have time to play with them.

  • Get unicode glyphs to show properly. (╯°□°)╯︵ ┻━┻
  • Creating slides with org-mode and Reveal.js. Finally, a sane way to create a presentation :)
  • Anzu: Improves incremental search. Anzu shows the number of matches found, and how many matches remain in the document. Anzu in action Anzu in action
  • Emacs as a Django IDE: I tried this out briefly, but didn’t end up sticking with it. It seemed too heavyweight. I’m probably just too used to my own workflow. I still think it’s an interesting idea and would like to try it again sometime.
  • Travel back and forth in git history: I’m addicted to magit mode, to the point that the only git command I use manually nowadays is git clone. I do everything else in magit. Magit has a magit-blame-mode which shows when each line in a file was changed and by whom. git-timemachine seems to take that a step further by visiting each revision, one by one. I can think of a few times where that would have been useful.
  • Search StackOverflow from inside Emacs: Enough said.
  • Emacs RSS client: Still using NewsBlur, but totally miss Google Reader. Maybe I need to try this one out.
  • Sacha Chua’s minicourse on Elisp: Have to read through this someday!

Jul 22, 2014 - 2 minute read - Comments - programming

Pygments on Arch Linux

I wrote my first blog post in a little while (ok, ok… 18 months) yesterday and when I tried to generate the post, it failed. Silently failed, which is the worst kind of failure. I’m still not sure why it was silent, but I eventually was able to force it to show me an error message:

/home/vinod/.rbenv/versions/1.9.3-p286/lib/ruby/gems/1.9.1/gems/pygments.rb-0.3.4/lib/pygments/popen.rb:354:in `rescue in get_header': Failed to get header. (MentosError)
	from /home/vinod/.rbenv/versions/1.9.3-p286/lib/ruby/gems/1.9.1/gems/pygments.rb-0.3.4/lib/pygments/popen.rb:335:in `get_header'
	from /home/vinod/.rbenv/versions/1.9.3-p286/lib/ruby/gems/1.9.1/gems/pygments.rb-0.3.4/lib/pygments/popen.rb:232:in `block in mentos'
	from /home/vinod/.rbenv/versions/1.9.3-p286/lib/ruby/1.9.1/timeout.rb:68:in `timeout'
	from /home/vinod/.rbenv/versions/1.9.3-p286/lib/ruby/gems/1.9.1/gems/pygments.rb-0.3.4/lib/pygments/popen.rb:206:in `mentos'
	from /home/vinod/.rbenv/versions/1.9.3-p286/lib/ruby/gems/1.9.1/gems/pygments.rb-0.3.4/lib/pygments/popen.rb:189:in `highlight'
	from /home/vinod/dev/ `pygments'
	from /home/vinod/dev/ `highlight'
	from /home/vinod/dev/ `block in render_code_block'
	from /home/vinod/dev/ `gsub'
	from /home/vinod/dev/ `render_code_block'
	from /home/vinod/dev/ `pre_filter'
	from /home/vinod/dev/ `pre_render'
	from /home/vinod/dev/ `block in pre_render'
	from /home/vinod/dev/ `each'
	from /home/vinod/dev/ `pre_render'
	from /home/vinod/dev/ `do_layout'
	from /home/vinod/.rbenv/versions/1.9.3-p286/lib/ruby/gems/1.9.1/gems/jekyll-0.12.0/lib/jekyll/post.rb:195:in `render'
	from /home/vinod/.rbenv/versions/1.9.3-p286/lib/ruby/gems/1.9.1/gems/jekyll-0.12.0/lib/jekyll/site.rb:200:in `block in render'
	from /home/vinod/.rbenv/versions/1.9.3-p286/lib/ruby/gems/1.9.1/gems/jekyll-0.12.0/lib/jekyll/site.rb:199:in `each'
	from /home/vinod/.rbenv/versions/1.9.3-p286/lib/ruby/gems/1.9.1/gems/jekyll-0.12.0/lib/jekyll/site.rb:199:in `render'
	from /home/vinod/.rbenv/versions/1.9.3-p286/lib/ruby/gems/1.9.1/gems/jekyll-0.12.0/lib/jekyll/site.rb:41:in `process'
	from /home/vinod/.rbenv/versions/1.9.3-p286/lib/ruby/gems/1.9.1/gems/jekyll-0.12.0/bin/jekyll:264:in `<top (required)>'
	from /home/vinod/.rbenv/versions/1.9.3-p286/bin/jekyll:23:in `load'
	from /home/vinod/.rbenv/versions/1.9.3-p286/bin/jekyll:23:in `<main>'

Professor Google tells me that this happens when you try to run the pygments.rb library in a Python 3 environment. (pygments.rb is a Ruby wrapper around the Python Pygments library). The fix is to run the code in a Python2 virtualenv. I guess the last time I updated my blog, Arch still had Python2 as the system default. No, I don’t want to check how long ago that was.

$ mkvirtualenv -p `which python2` my_blog
(my_blog)$ bundle exec rake generate

So now I’m running a Ruby command in a Ruby environment (rbenv) inside a Python 2 virtualenv. Maybe it’s time to switch blog tools again…

Jul 21, 2014 - 2 minute read - Comments - django programming

How to create test models in Django

It’s occasionally useful to be able to create a Django model class in your unit test suite. Let’s say you’re building a library which creates an abstract model which your users will want to subclass. There’s no need for your library to subclass it, but your library should still test that you can create a subclass and test out its features. If you create that model in your file, then Django will think that it is a real part of your library and load it whenever you (or your users) call syncdb. That’s bad.

The solution is to create it in a file within your Django app. If it’s not in, Django won’t load it during syncdb.

from django.db import models
from django.test import TestCase

from .models import MyAbstractModel

class MyTestModel(MyAbstractModel):
    name = models.CharField(max_length=20)

class AbstractTest(TestCase):
    def test_my_test_model(self):

A problem with this solution is that I rarely use a single file. Instead we use multiple test files collected in a tests package. If you try to create a model in tests/, then this approach fails because Django tries to create the model in an application named tests, but there is no such app in INSTALLED_APPS. The solution is to set app_label to the name of your app in an inner Meta class.

from django.db import models
from django.test import TestCase

from ..models import MyAbstractModel

class MyTestModel(MyAbstractModel):
    name = models.CharField(max_length=20)

    class Meta:
        app_label = 'myappname'

class AbstractTest(TestCase):
    def test_my_test_model(self):

Oh, and I almost forgot… if you use South, this might not work, unless you set SOUTH_TESTS_MIGRATE to False in your settings file.

Comments and corrections welcome!

Jan 21, 2013 - 6 minute read - Comments - medicine family

Peanut Allergy, a personal clinical review

A Vignette

A 22-month-old previously healthy girl was brought to the emergency room in respiratory distress. She had been well until the day of presentation. Shortly after disembarking from an airline flight, she developed cough with one episode of emesis. En route to the emergency room, her breathing became labored and she developed hives. She was diagnosed as having an allergic reaction and treated aggressively with IV methylprednisolone, IV diphenhydramine, inhaled albuterol, and oxygen. Mechanical ventilation was not required and her symptoms improved rapidly. She was monitored in the emergency room for an extended period and discharged in stable condition with a prescription for diphenhydramine and methylprednisolone solutions. No specific allergen was identified. She was seen by a pediatric allergist where she was found to have a 6 mm response to peanut on skin prick testing, and a peanut-specific IgE level of 52 kU/L. She was diagnosed as having severe peanut allergy and avoidance was strongly recommended.


Peanut allergy affects between 0.4 and 2.0% of children. Prevalence has been increasing in recent years, but the reason for this increase is not well understood. If reactions are identified immediately, treatment is usually effective. Despite this, about 200 people die each year from peanut reactions. Biphasic reactions, in which the initial reaction subsides, but then recurs within a few hours, are common. The second portion of a biphasic reaction is often less treatable. Because of this risk, patients should be monitored for at least 4 hours after a reaction. Patients who successfully treat their own reactions with subcutaneous epinephrine should still seek medical attention, again to monitor for biphasic reactions.

The gold standard for diagnosis is a double blind placebo controlled food challenge (DBPCFC), but this is rarely done. A diagnosis can usually be reliably made with clinical history, skin prick testing and a peanut-specific IgE blood test (psIgE). A skin prick test involves pricking the skin with a needle that is exposed to peanut extract and measuring the allergic rash that develops. The psIgE blood test measures the amount of antibody against peanut proteins. The higher it is, the more allergic the patient is. A positive skin test or psIgE is not sufficient for the diagnosis. Some type of reaction to peanuts is required (either spontaneous, or via monitored food challenge).

Peanut allergy was previously felt to be permanent, but recent evidence documents that 20% of children outgrow their allergy. More than 60% of patients with an psIgE level of less than 5 passed a food challenge. IgE levels correlate inversely with likelihood of resolution, but this correlation is by no means perfect. Some patients with undetectable psIgE levels will fail a challenge, while some patients with very high psIgE levels will pass. One expert recommends waiting until the age of 4 to consider a challenge, and to use a cutoff psIgE level of less than 5 kU/L in most patients. Recurrence of peanut allergy after resolution is rare, but is associated with the lack of regular peanut intake after resolution of the allergy.

Controversy exists regarding risk factors for developing peanut allergy. The AAP initially recommended that peanut not be introduced early in a child’s life, but rescinded that advice given the lack of evidence behind it. This leaves a void for parents as there is no recommendation to withold or introduce peanuts to toddlers. It is also not clear if maternal peanut intake during pregnancy or breast feeding increases the risk of developing peanut allergy in children. Studies have been conflicting, so more definitive studies are underway. No evidence-based advice can be given one way or the other to pregnant or breastfeeding women, but at the minimum, there is no good evidence to support avoiding peanuts.

The mainstay of treatment is education. Parents should be taught not only how to identify peanuts in foods, but to identify situations which are high-risk for exposure to peanuts. Avoidance of buffets, ice-cream parlors, and bakeries is recommended because the risk of cross-contamination in those setting is high. Religious reading of food labels is important. Patients who are allergic to peanuts are often also allergic to tree nuts, despite the dissimilarity in those food types. Highly refined peanut oil is usually NOT a risk, but cold-pressed peanut oil is not safe.

Most importantly, an epi-pen should be carried at all times and extra doses should be left at daycare facilities and schools. All caregivers should be comfortable giving an epi-pen injection and this should be provided at the first sign of a systemic allergic reaction. Waiting to give diphenhydramine or other therapies a “chance to work” can be dangerous. Follow a Food Allergy Action Plan Over 80% of people who died from allergic reactions were not given appropriate instructions on how to prevent future reactions. Most mortality occurs because of late treatment. If you take one thing away from this long post, keep an epi-pen handy and use it liberally.

Trials are underway investigating various oral-based immunotherapies, but none are strongly recommended at this point. Injection immunotherapy has not been found to be useful.

Personal Comment

So, the 22 month old above is Anika and she gave us the scare of our life a couple years ago. The scary part now is that we never saw her actually eat a peanut, so we’re especially vigilant now. Fortunately, Anika seems to understand the importance of avoiding peanuts. When she was diagnosed, I did some research but never documented my notes. She recently had another allergist appointment, so I reviewed those notes and any new data. I figured I should write up my notes so I wouldn’t have to review this again from scratch every year. I didn’t intend it to come out so ‘medical-speaky’, but there it is. Anika and Kavi are sitting next to me as I write this and they want to give some information to you, too.

Kavi says:

“Always ask at the restaurant if things have peanuts and make foods at home that do not have peanuts. Always check if there are peanuts in every food you eat.”

Anika says:

“We might have to go to the doctor if we eat peanuts. If I eat peanuts by accident, my teacher will give me a Happy-Pen (epi-pen).”


  1. Sampson HA. Peanut Allergy. N Engl J Med, 2002;346;1294-8.
  2. Fleischer DM, et. al. The natural progression of peanut allergy: Resolution and the possibility of recurrence. J Allergy Clin Immunol, 2003;112;183-9.
  3. O’B Hourihane J. Peanut Allergy. Pediatr Clin N Am, 2011;58;445-57.
  4. Rinaldi M, et. al. Peanut allergy diagnoses among children residing in Olmstead County, Minnesota. J Allergy Clin Immunol, 2012:130:945-50.
  5. Burks AW. Early peanut consumption: postpone or promote? J Allergy Clin Immunol. 2009:123:417-423.


Here’s a medline feed URL that I monitor which should show new studies or reviews related to this topic.

Jan 17, 2013 - 3 minute read - Comments - gtd

Priorities and GTD

Pre-GTD task management systems recommended that you assign a priority to each task. When looking at a list of tasks, you then choose the ones with the highest priority. David Allen (author of GTD) includes ‘priority’ as one of the things that you should consider when you are deciding what to do, but only after you have considered context, time available, and energy available. Which makes sense. As Merlin Mann says, it doesn’t matter if mowing your lawn is your highest priority when you’re sitting in an airplane. Only if you’re in the right context with the right amount of time and the right amount of energy, should you consider which of the remaining available tasks is highest priority.

The risk, though, is that there are so many little ‘easy’ tasks that I could do instead of important, ‘hard’ tasks. I’m also really good at convincing myself that I don’t have the energy available to do harder tasks when I really should. So when I get back into GTD, I process all the things hiding in the back of my brain which leads to the creation of lots of simple tasks. Google this, buy that, throw away the other thing. But the two things that I really want to be doing more are writing and programming. Both of those take blocks of uninterrupted time and effort, so I end up subconsciously shying away from them. I actually feel better about it, too, because now I have all these other tasks that I can do instead. So, I feel productive, even when ignoring the more important things that I should be doing. I’m moving more efficiently, but in the wrong direction.

I’ve avoided that a little bit this time by doing a little bit of prioritization each night before I go to bed. I look through my todo list and pick about 3-5 things that I want to get done the next day. I tag those with the ‘today’ tag and then the next morning I look at that list and start working on those things before doing anything else. I’ve heard this trick from a lot of people before, so I don’t remember who I heard it from first.

So far it’s been effective for me. The problem always starts again though when I go back on service, because then I can only really focus on one thing and that is getting my work done at the hospital. We’ll see if I can somehow figure out a way to get at least one non-work thing done on each of those days.