so, it's come to this...

by Michael

Aug 1, 2014

I do a podcast with a couple other guys. As we're recording the show, I like to create the show notes live so that they're most accurate and save myself time later. We frequently mention products on Amazon, apps on iTunes, and regular old-fashioned links. I created the following macro for myself so that after I've navigated to the webpage of the link in Safari, I can automatically create a Markdown-style link in my text editor, including optional Amazon and iTunes affiliate links if appropriate. The affiliate links are added using python scrips from Dr. Drang and Brett Kelly.

The macro can be triggered by the string mdlink of the hotkey ⇧⌃⌥⌘p.

Since I write all my show notes in nvALT and my workflow is to find the webpage in Safari, the first part of the macro activates nvALT. The topmost Safari URL is then saved to the clipboard. The clipboard is then parsed by two If statements looking to see if it is an Amazon or iTunes link, and if it is add the affiliate code. Finally, the text for the Markdown link in inserted using the Safari title as the text for the link.

Macro Text

My Macros
mdlink
Triggered by any of the following:
The exact case string ‘mdlink’ is typed (then deleted)
The Hot Key ⌃⌥⇧⌘P is pressed
Will execute the following actions:
Activate nvALT
Set Clipboard to Text
%SafariURL%
If All Conditions Met
The clipboard text contains ‘itunes.apple.com’
Execute the Following Actions:
Execute Shell Script

#!/usr/bin/python

# Via Dr. Drang
# http://www.leancrew.com/all-this/2013/08/new-apple-affiliate-link-scripts/

from subprocess import check_output
from sys import stdout

# My affiliate ID.
myID = '11l4RT'

# Get the URL from the clipboard.
clipURL = check_output('pbpaste')

# Add my ID and the partnerId parameter to the URL. If there are already
# queries, add them as additional ones; if not, add them as the only ones.
if '?' in clipURL:
itemURL = '%s&at=%s' % (clipURL, myID)
else:
itemURL = '%s?at=%s' % (clipURL, myID)

# Write it out
stdout.write(itemURL)

Save trimmed to Clipboard.

If All Conditions Met
The clipboard text contains ‘amazon.com’
Execute the Following Actions:
Execute Shell Script

#!/usr/bin/env python

# (c) 2012 Brett Kelly.
# Licensed under the MIT license
# http://www.opensource.org/licenses/mit-license.php
# 
# Some edits from the original to get to to work in KM - ML

import re
import sys
from urlparse import urlparse
from subprocess import check_output

## PUT YOUR AFFILIATE CODE HERE
affcode = 'violeneces-20'

rawurl = check_output('pbpaste')

## Get the base url without all of the query string malarky
baseurl = rawurl.split('?')[0] 

try:
parts = urlparse(baseurl)
except Exception:
sys.stdout.write(rawurl)
raise SystemExit

## Make sure it's actually an Amazon URL
amazonre = re.compile('[www\.]{0,1}amazon\.')

if not amazonre.search(parts.netloc):
# Not an amazon URL, return whatever was passed initially
sys.stdout.write(rawurl)
raise SystemExit

# Format the simpler URL and append the affiliate code
goodurl = "%s://%s%s?tag=%s" % (parts.scheme,parts.netloc,parts.path,affcode)

# Write formatted URL to STDOUT
sys.stdout.write(goodurl)

Save trimmed to Clipboard.
Insert Text by Pasting
[%SafariTitle%](%CurrentClipboard%)

Macro Image

Macro Image

Download the macro.

Jul 29, 2014

I create a lot of macros with KM and I like to share them both on this blog as well as in the KM forums1. Taking screenshots of the macro is an important part of this sharing and has been very useful to me as I have learned to use KM from my betters in the community. Previously, I would use the standard screen shot functions of OS X, but with very long macros that did not all fit on one screen there was no elegant solution.

I believe it was in 6.0 that Peter added the ability to copy a macro as an image. I failed to pay much attention to this at the time in light of all the other great functionality, but recently put it to use.

The copy as an image puts the highlighted portion of your macro into the clipboard. By itself, this isn't very useful because I need an actual image to upload to the blog, and as far as I can tell that functionality is not built in2.

So I created a macro.

Macro description

You can either select individual parts of the macro you want to share, or select nothing and the whole macro will be used.

Using a hotkey, trigger the Create Screenshot Macro.

  1. Select the copy as image from the menu
  2. Prompt the user for the file name
  3. Save the file to the desktop

There's also a copy as text if you want to just share the text of the macro. I prefer to write my own descriptions because I think it's more useful, but including the text is nice too. See below.

Macro text

Create screenshot of KM macro
Triggered by any of the following:
The Hot Key ⌃⌥⇧⌘M is pressed
Will execute the following actions:
Select Menu Item in Keyboard Maestro
Select: Edit ⇢ Copy as ⇢ Copy as Image
Stop macro if menu cannot be selected.
Prompt for User Input ‘File Name’
What do you want to call the file?
Input the following variables:
File Name
Finish with the following buttons:
OK
Cancel (cancel macro)
Store button pressed in variable ‘Result Button’.
Write Clipboard
To file: ~/Desktop/%Variable%File Name%.jpg
With format JPEG).

Macro image

Macro Image 

Download the macro.

Room for improvement

I would like the ability to automatically name the jpg with the name of the macro.


  1. If you use KM and aren't in the forums, you are really missing out. 

  2. Is this an oversight, am I missing it, or is this deliberate on Peter's part because he wants the user to create the macro by themselves? 

May 8, 2014

Me: Do you need a haircut?

Boy: No! You need a haircut!

Me: You're right. I have one scheduled for this Saturday. What should I do?

Boy: You have emo hair. You look like Justin Bieber.

Me: Should I go bald? Should I cut it short?

Boy: No!

Me: How about some lines or maybe lightning bolts?

Boy: No. Don't do anything weird. Just be normal.

May 2, 2014

Conversations I probably shouldn't have with my employees:

Them: May I see you in your office with the door closed for two minutes?
Me: I take a lot longer than that!
Them: That's not what your wife says.

Apr 16, 2014

Steven Novella:

While drug reps are certainly in sales, I have only personally seen a drug rep step over the line on one occasion. A new rep, who was clearly green, actually said to me and my colleague, “what would it take to get you to prescribe more of this drug?” I don’t think he was suggesting anything like bribery, but the statement was clearly inappropriate.

I've been asked that question before. Based on context, I didn't assume I was being bribed. I took it as "what medical and financial factors are keeping you from using my medication over another?"

My answers have been along the lines of "make it cheaper and/or show evidence of superiority in head-to-head trials".

Interestingly, the £100 that was offered to the physicians in the referenced article is a lot less than the financial incentive when a patient demands oxycodone while holding a patient satisfaction survey.

Mar 18, 2014

Is your room clean?

No.

Do I want it clean?

Yes.

...

Why are you still here?

Mar 4, 2014

I happened to learn that Dragon Dictate is being upgraded. I own MacSpeech Dictate Medical, which was bought out by Nuance, so I decide to check it out and possibly upgrade.

Wow. $1000. That's a lot for something I don't use that much.

But look, I can upgrade for only $500. I'll check that out.

Just need to log into my account.

Look, it's only $300. I'll totally buy that.

WTF? That says $600, and it's for MacSpeech Medical not Dragon Dictate Medical.

To be perfectly clear. Each screen shot was take in a single series of links, one to the other. The price kept changing. The product kept changing. What is going on here?

I have no idea what the problem is, but I'm not comfortable with moving forward with this purchase.

Mar 1, 2014

I hope you're sick.

I know. I'm an asshole, but it's true. Like a soldier in a time of peace or a fireman without a fire, I'm basically useless if you're healthy. I spend all of my down time studying disease and treatments, and the worse you are, the more I (and medical science) likely knows about it.1 So I hope you are sick; I'll be able to fix you.

I wasn't always like this. Initially, I hoped everyone was healthy because I didn't have a clue what I was doing and if you were healthy, my ignorance wouldn't show in front of my attending. But eventually I learned my craft, and this fear dissipated. Still, as a decent human being, I always hoped my patients were not sick. Who could wish affliction on another?

Then they happened.

I would guess that they are a small part of the population, but given selection bias are a large percent of new consults. They are convinced that something is seriously wrong with them. They are sure they have some "rare" disease.2 They are not comforted by the numerous negative tests and consults they have had. They spend the first 10 minutes of the visit insulting their prior doctors and praising you.3 They are armed with facts sans wisdom from the internet.4

You spend a lot of time listening to them tell you their story, complete with whole chapters of unrelated tangents like a Hugo novel. You do the most detailed physical examination ever. You pour over decades of old medical reports mixed in with their personal notes and WebMD printouts. You order expensive and certainly unnecessary testing. And at the end you explain it all in great detail, hoping beyond hope that they'll be comforted with the news that any sane person would want to hear:

You're healthy.

But they aren't. They yell and spit. They threaten to sue and say they won't pay your bill. They storm out of the office terrorizing the other patients.

And then the complaint letters come, to you, to your manager, to the insurance company, the hospital, the medical board, and Yelp.

  • He didn't care.
  • He didn't listen.
  • He dismissed me.
  • He's rude.
  • He's incompetent.

Defend yourself, you are told. How?

And so, when I read a chart the night before a consult, and my years of professional insight starts telling me there is no disease here, I again hope against all hope that I will walk in to that room tomorrow and see synovitis, or tophi, or discoid lesions. I hope that I can tell you that you have a terrible, destructive, life-long illness requiring toxic chemotherapy. I hope you are sick.

That's what you want, right?


  1. We can cure polio but not the cold. 

  2. Or, frequently not rare, they just think it is. 

  3. Soon they will be insulting me to their next doctor. 

  4. Yes, photosensitivity is a symptom of lupus, but that is skin sensitivity, not eye sensitivity. You can take off your sunglasses now. 

Feb 21, 2014

I wanted a better idea of how much time I was spending per visit. This data could also be used when billing by time. I wrote three1 small macros to complete this task.

All three macros use the same trigger, ⌘⌥⌃⇧b.2 This brings up a menu for me to choose what part of the macro-system I want.

All three macros also write data to the same log file on my desktop.

Macro 1 - Start of the visit

I trigger the first macro when I'm walking into the room. It does three simple tasks.

  1. Stores the current time of the day in milliseconds to a variable.
  2. Displays the current time preceded by the phrase "Visit started at " in a small window on the screen
  3. Writes the current time preceded by the phrase "Visit started at " in the log file

Macro 2 - End of the visit

I trigger the second macro when I'm walking out of the room. It does 5 simple tasks.

  1. Stores the current time of the day in milliseconds to a variable.3
  2. Calculates the length of the visit, rounded to the nearest minute.
  3. Appends text to the log file with the end time of the visit and the total visit length.
  4. Pastes a template phrase required by the government and insurance companies into my clinic note with the length of the visit as calculated above.
  5. Displays the various code levels on the screen for each time interval, so I know what billing level to code the visit.

Macro 3 - Set a new date

This is just simply appends a break and a date header to the log file. I trigger this before my first visit each morning in order to keep the log file neat and organized. I could automate this, but for each way that I thought to do so, I thought of several possible fail points.


  1. I'd previously tried to do this with one macro, but had too many complications and bugs. Seperating them out is equivalent to writing well-contained functions. 

  2. ⌘⌥⌃⇧ is mapped to the caps lock key on all my computer, so it is just one key. 

  3. Now I have the start time and the end time, in milliseconds, stored to separate variables. 

Feb 7, 2014

I just started using a second monitor at work1. The problem was that every time I would plug my Macbook Air into the monitor via the Thunderbolt connection, the previously arranged window position was lost. I have a very simple arrangement: my text editor maximized on my laptop, and the EMR maximized on the external screen.

Keyboard Maestro to the rescue.

The main actions just move the desired application window to the top left of the respective screens, SCREEN(1, for the laptop screen and SCREEN(2, for the external monitor. The +22 for the downward direction moves the window down below the menu bar, which is 22 pixels tall. The -22 for the window height subtracts the menu height from the total window height so you don't lose the last 22 pixels below the screen.

The hardest part of this macro is figuring out that you can type these commands into the action. Normally the action looks like this:

It looks like you can only input numbers2, but if you start typing, it will transform to this:

Update: From the Yahoo Keyboard Maestro user group, I found more information on picking which screen you’re giving instructions to.

  • Numbers: designates the screens from left to right. 1, 2, 3…
  • Main and Secondary: main refers to whatever screen is currently being used by the active window/application
  • Internal and External: the iMac/laptop display versus the external display

You can use these various designations depending on your use case. For example, I was originally using numbers, but that meant I had to have two separate macros for making a window maximized, one for the main monitor and one for the external, but by changing the 1 and 2 to main, it will maximize the window that has the focus on that screen. They have slightly different meanings, but this allows different functionality.


  1. The EMR we use is so poorly arranged that a larger screen is needed to make up for the poor design. 

  2. This has screwed me up several times when trying to create an action. It is not always obvious that another type of input is possible.