Lessig MayOne SuperPAC Email UX

I just got the MayOne SuperPAC email announcing it’s hit $500,000 in funding.

I backed the SuperPAC as I’m pretty disenfranchised with the current “pay-to-play” political landscape, which I don’t feel serves the public interest. </rant> While reading, I noticed that the messaging and on the SuperPAC email is really effective.

ImageLet’s explore the email and how this email is effective at delivering a message and inciting action:

1 – Concise, at 154 words and < 700 characters.

ImageJeez! Half of the emails an audience gets are long, while the other half are a nonsensical mish-mash of photos. How am I supposed to read and really digest any of that quickly? Averaging 10 emails I recently received, I found there were an average of 322 characters per email. SuperPAC comes in far below the average word count making it quick to read.

2 – The Email Subject: This is incredible.

It’s victorious, uplifting, and a firm statement of wonder. As the recipient of this email the audience has played a key role in making this campaign an unbelievable success already.

3 – Positive Language and Tailored Message

“Wow,” and “in just five days, we’ve raised more than $500,000!” are very positive, and again add to the feeling that something bigger is going on. The wording is also accessible, so the audience understands what the message is trying to communicate.

4 – Low Stakes Actionable Sharing

“All you need to do is convince one friend to join you in donating. There are easy ways to do this, like sharing on Twitter and Facebook.” If someone signed up for the SuperPAC, it’s likely they want to tell people about it. Leveraging social sharing is a great way to build buzz and social networks are an easy way to share. The links embedded in the email make sharing incredibly easy, just two clicks away.

But the best way is to forward this email with a personal note to friends and family who care about this issue too.” Easy ways to share are fine, but even more effective than blasting a social network is sending a targeted email to someone who shares the audiences viewpoint.

And hey look, SuperPAC has started the email already: “PS — Need some guidance on how to craft your personal message? We’ve posted an example here.

5 – Friendly Hassling

“Think you can do that?” is something a friend would say. Closing with a bit of a nudge and a “c’mon” wink is a great way to encourage the audience to act while staying congenial.

6 – Thinking of the Future

“More hopeful than ever, Lessig” shows that the author believes in the audience. There’s hope, which is a good thing.

Final Thoughts

Delivering an impactful message with concision and thought toward the desired outcome is critical when composing any message with a specific audience in mind. Please share any examples you’ve found of this sort of effective communication.

If you’d like to learn more about effective communication, the fine folks at MailChimp have put together a free PhD on the topic over at http://mailchimp.com/resources/

Advertisements

Google Code Basic Python List2.py

Did it. I keep wondering if I’ve not done the assignment correctly.

#!/usr/bin/python -tt
 # Copyright 2010 Google Inc.
 # Licensed under the Apache License, Version 2.0
 # http://www.apache.org/licenses/LICENSE-2.0
# Google's Python Class
 # http://code.google.com/edu/languages/google-python-class/
# Additional basic list exercises
# D. Given a list of numbers, return a list where
 # all adjacent == elements have been reduced to a single element,
 # so [1, 2, 2, 3] returns [1, 2, 3]. You may create a new list or
 # modify the passed in list.
 def remove_adjacent(nums):
 nums = list(set(nums))
 return nums
# E. Given two lists sorted in increasing order, create and return a merged
 # list of all the elements in sorted order. You may modify the passed in lists.
 # Ideally, the solution should work in "linear" time, making a single
 # pass of both lists.
 def linear_merge(list1, list2):
 finalist = list1 + list2
 finalist.sort()
 return finalist
# Note: the solution above is kind of cute, but unforunately list.pop(0)
 # is not constant time with the standard python list implementation, so
 # the above is not strictly linear time.
 # An alternate approach uses pop(-1) to remove the endmost elements
 # from each list, building a solution list which is backwards.
 # Then use reversed() to put the result back in the correct order. That
 # solution works in linear time, but is more ugly.
# Simple provided test() function used in main() to print
 # what each function returns vs. what it's supposed to return.
 def test(got, expected):
 if got == expected:
 prefix = ' OK '
 else:
 prefix = ' X '
 print '%s got: %s expected: %s' % (prefix, repr(got), repr(expected))
# Calls the above functions with interesting inputs.
 def main():
 print 'remove_adjacent'
 test(remove_adjacent([1, 2, 2, 3]), [1, 2, 3])
 test(remove_adjacent([2, 2, 3, 3, 3]), [2, 3])
 test(remove_adjacent([]), [])
print
 print 'linear_merge'
 test(linear_merge(['aa', 'xx', 'zz'], ['bb', 'cc']),
 ['aa', 'bb', 'cc', 'xx', 'zz'])
 test(linear_merge(['aa', 'xx'], ['bb', 'cc', 'zz']),
 ['aa', 'bb', 'cc', 'xx', 'zz'])
 test(linear_merge(['aa', 'aa'], ['aa', 'bb', 'bb']),
 ['aa', 'aa', 'aa', 'bb', 'bb'])
if __name__ == '__main__':
 main()

More Google Code School Exercises

This one is from list1.py:

#!/usr/bin/python -tt
# Copyright 2010 Google Inc.
# Licensed under the Apache License, Version 2.0
# http://www.apache.org/licenses/LICENSE-2.0
# Google's Python Class
# http://code.google.com/edu/languages/google-python-class/
# Basic list exercises
# Fill in the code for the functions below. main() is already set up
# to call the functions with a few different inputs,
# printing 'OK' when each function is correct.
# The starter code for each function includes a 'return'
# which is just a placeholder for your code.
# It's ok if you do not complete all the functions, and there
# are some additional functions to try in list2.py.
# A. match_ends
# Given a list of strings, return the count of the number of
# strings where the string length is 2 or more and the first
# and last chars of the string are the same.
# Note: python does not have a ++ operator, but += works.
def match_ends(words):
 total = 0
 print 'Of the following:',
 for i in words:
 print '\'%s\'' % i,
 if len(i) >= 2:
 fl = i[0]
 ll = i[-1]
 if fl == ll:
 total += 1
 print '%i are >= two characters and have first/last characters that are the same.' % total
 return

# B. front_x
# Given a list of strings, return a list with the strings
# in sorted order, except group all the strings that begin with 'x' first.
# e.g. ['mix', 'xyz', 'apple', 'xanadu', 'aardvark'] yields
# ['xanadu', 'xyz', 'aardvark', 'apple', 'mix']
# Hint: this can be done by making 2 lists and sorting each of them
# before combining them.
def front_x(words):
 xwords = []
 owords = [] for w in words:
 if w[0] == 'x':
 xwords.append(w)
 else:
 owords.append(w) xwords.sort()
 owords.sort() print xwords + owords
 return

 

# C. sort_last
# Given a list of non-empty tuples, return a list sorted in increasing
# order by the last element in each tuple.
# e.g. [(1, 7), (1, 3), (3, 4, 5), (2, 2)] yields
# [(2, 2), (1, 3), (3, 4, 5), (1, 7)]
# Hint: use a custom key= function to extract the last element form each tuple.
def sort_last(tuples):
 def sortem(s):
 return s[-1]
 
 print sorted(tuples, key=sortem)
 return

# Simple provided test() function used in main() to print
# what each function returns vs. what it's supposed to return.
def test(got, expected):
 if got == expected:
 prefix = ' OK '
 else:
 prefix = ' X '
 print '%s got: %s expected: %s' % (prefix, repr(got), repr(expected))

# Calls the above functions with interesting inputs.
def main():
 print 'match_ends'
 test(match_ends(['aba', 'xyz', 'aa', 'x', 'bbb']), 3)
 test(match_ends(['', 'x', 'xy', 'xyx', 'xx']), 2)
 test(match_ends(['aaa', 'be', 'abc', 'hello']), 1)
print
 print 'front_x'
 test(front_x(['bbb', 'ccc', 'axx', 'xzz', 'xaa']),
 ['xaa', 'xzz', 'axx', 'bbb', 'ccc'])
 test(front_x(['ccc', 'bbb', 'aaa', 'xcc', 'xaa']),
 ['xaa', 'xcc', 'aaa', 'bbb', 'ccc'])
 test(front_x(['mix', 'xyz', 'apple', 'xanadu', 'aardvark']),
 ['xanadu', 'xyz', 'aardvark', 'apple', 'mix'])

 print
 print 'sort_last'
 test(sort_last([(1, 3), (3, 2), (2, 1)]),
 [(2, 1), (3, 2), (1, 3)])
 test(sort_last([(2, 3), (1, 2), (3, 1)]),
 [(3, 1), (1, 2), (2, 3)])
 test(sort_last([(1, 7), (1, 3), (3, 4, 5), (2, 2)]),
 [(2, 2), (1, 3), (3, 4, 5), (1, 7)])

if __name__ == '__main__':
 main()

Basic Google Code School Python Exercises

I don’t know if this is of interest to anyone but here are my Google Code School solutions to the basic python exercises string1.py and string2.py (my code in red):

string1.py
 #!/usr/bin/python -tt
 # Copyright 2010 Google Inc.
 # Licensed under the Apache License, Version 2.0
 # http://www.apache.org/licenses/LICENSE-2.0
# Google's Python Class
 # http://code.google.com/edu/languages/google-python-class/
# Basic string exercises
 # Fill in the code for the functions below. main() is already set up
 # to call the functions with a few different inputs,
 # printing 'OK' when each function is correct.
 # The starter code for each function includes a 'return'
 # which is just a placeholder for your code.
 # It's ok if you do not complete all the functions, and there
 # are some additional functions to try in string2.py.
 # A. donuts
 # Given an int count of a number of donuts, return a string
 # of the form 'Number of donuts: <count>', where <count> is the number
 # passed in. However, if the count is 10 or more, then use the word 'many'
 # instead of the actual count.
 # So donuts(5) returns 'Number of donuts: 5'
 # and donuts(23) returns 'Number of donuts: many'
 def donuts(count):
 if count <= 9:
 print 'Number of dounuts: %s' % count
 elif count >= 10:
 print 'Number of donuts: many'
 else:
 print 'That is not a valid integer.'
 return
 # B. both_ends
 # Given a string s, return a string made of the first 2
 # and the last 2 chars of the original string,
 # so 'spring' yields 'spng'. However, if the string length
 # is less than 2, return instead the empty string.
 def both_ends(s):
 if len(s) >= 3:
 fl = s[:2]
 ll = s[-2:]
 print fl + ll
 else:
 print ''
 return
 # C. fix_start
 # Given a string s, return a string
 # where all occurences of its first char have
 # been changed to '*', except do not change
 # the first char itself.
 # e.g. 'babble' yields 'ba**le'
 # Assume that the string is length 1 or more.
 # Hint: s.replace(stra, strb) returns a version of string s
 # where all instances of stra have been replaced by strb.
 def fix_start(s):
 fl = s[0]
 print s[0] + s[1:].replace(fl, '*')
 return
 # D. MixUp
 # Given strings a and b, return a single string with a and b separated
 # by a space '<a> <b>', except swap the first 2 chars of each string.
 # e.g.
 # 'mix', pod' -> 'pox mid'
 # 'dog', 'dinner' -> 'dig donner'
 # Assume a and b are length 2 or more.
 def mix_up(a, b):
 afc = a[:2] # Get the first two letters of A and assign to afc
 bfc = b[:2] # Get the first two letters of B and assign to bfc
 a = bfc + a[2:] # Append all but the first two characters of A to bfc
 # and assign it all to A
 b = afc + b[2:] # Append all but the first two characters of B to afc
 # and assign it all to B
 print '%s %s' % (a, b)
 return
 # Provided simple test() function used in main() to print
 # what each function returns vs. what it's supposed to return.
 def test(got, expected):
 if got == expected:
 prefix = ' OK '
 else:
 prefix = ' X '
 print '%s got: %s expected: %s' % (prefix, repr(got), repr(expected))
 # Provided main() calls the above functions with interesting inputs,
 # using test() to check if each result is correct or not.
 def main():
 print 'donuts'
 # Each line calls donuts, compares its result to the expected for that call.
 test(donuts(4), 'Number of donuts: 4')
 test(donuts(9), 'Number of donuts: 9')
 test(donuts(10), 'Number of donuts: many')
 test(donuts(99), 'Number of donuts: many')
print
 print 'both_ends'
 test(both_ends('spring'), 'spng')
 test(both_ends('Hello'), 'Helo')
 test(both_ends('a'), '')
 test(both_ends('xyz'), 'xyyz')
 print
 print 'fix_start'
 test(fix_start('babble'), 'ba**le')
 test(fix_start('aardvark'), 'a*rdv*rk')
 test(fix_start('google'), 'goo*le')
 test(fix_start('donut'), 'donut')
print
 print 'mix_up'
 test(mix_up('mix', 'pod'), 'pox mid')
 test(mix_up('dog', 'dinner'), 'dig donner')
 test(mix_up('gnash', 'sport'), 'spash gnort')
 test(mix_up('pezzy', 'firm'), 'fizzy perm')
 # Standard boilerplate to call the main() function.
 if __name__ == '__main__':
 main()

And this is the second one:

string2.py
# Copyright 2010 Google Inc.
# Licensed under the Apache License, Version 2.0
# http://www.apache.org/licenses/LICENSE-2.0
# Google's Python Class
# http://code.google.com/edu/languages/google-python-class/
# Additional basic string exercises
# D. verbing
# Given a string, if its length is at least 3,
# add 'ing' to its end.
# Unless it already ends in 'ing', in which case
# add 'ly' instead.
# If the string length is less than 3, leave it unchanged.
# Return the resulting string.
def verbing(s):
 if len(s) >= 3:
 if s[-3:] == 'ing':
 print s + 'ly'
 else:
 print s + 'ing'
 else:
 print s
 return
# E. not_bad
# Given a string, find the first appearance of the
# substring 'not' and 'bad'. If the 'bad' follows
# the 'not', replace the whole 'not'...'bad' substring
# with 'good'.
# Return the resulting string.
# So 'This dinner is not that bad!' yields:
# This dinner is good!
def not_bad(s):
 wnot = s.find('not') # Find the index of not
 wbad = s.find('bad') # Find the index of bad
 if wnot < wbad: # if the index of not is lower 
 # than the index of bad...
 wbad += 3 # Add 3 to the index of bad
 print s.replace(s[wnot:wbad], 'good') # and replace everything between
 # wnot and wbad with 'good'
 else:
 print s # else print the string.
 return
# F. front_back
# Consider dividing a string into two halves.
# If the length is even, the front and back halves are the same length.
# If the length is odd, we'll say that the extra char goes in the front half.
# e.g. 'abcde', the front half is 'abc', the back half 'de'.
# Given 2 strings, a and b, return a string of the form
# a-front + b-front + a-back + b-back
def front_back(a, b):
 fa = ''
 fb = ''
 ba = ''
 bb = ''
 la = len(a)
 lb = len(b)
 if la % 2 == 0:
 eveninta = la / 2
 fa = a[:eveninta]
 ba = a[-eveninta:]
 else: 
 oddintba = la / 2
 oddintfa = oddintba+ 1
 fa = a[:oddintfa]
 ba = a[-oddintba:] if lb % 2 == 0:
 evenintb = lb / 2
 fb = b[:evenintb]
 bb = b[-evenintb:]
 else: 
 oddintbb = lb / 2
 print 'this ' + str(oddintbb)
 oddintfb = oddintbb + 1
 fb = b[:oddintfb]
 bb = b[-oddintbb:]
 print fa + fb + ba + bb
 return
# Simple provided test() function used in main() to print
# what each function returns vs. what it's supposed to return.
def test(got, expected):
 if got == expected:
 prefix = ' OK '
 else:
 prefix = ' X '
 print '%s got: %s expected: %s' % (prefix, repr(got), repr(expected))

# main() calls the above functions with interesting inputs,
# using the above test() to check if the result is correct or not.
def main():
 print 'verbing'
 test(verbing('hail'), 'hailing')
 test(verbing('swiming'), 'swimingly')
 test(verbing('do'), 'do')
print
 print 'not_bad'
 test(not_bad('This movie is not so bad'), 'This movie is good')
 test(not_bad('This dinner is not that bad!'), 'This dinner is good!')
 test(not_bad('This tea is not hot'), 'This tea is not hot')
 test(not_bad("It's bad yet not"), "It's bad yet not")
print
 print 'front_back'
 test(front_back('abcd', 'xy'), 'abxcdy')
 test(front_back('abcde', 'xyz'), 'abcxydez')
 test(front_back('Kitten', 'Donut'), 'KitDontenut')
if __name__ == '__main__':
 main()

So there are my solutions.

Absence

I haven’t been posting much recently which is quite a shame. I should keep track of my projects better.

Here are a few things I’ve done in the last six months or so:

  • Lockpicking class with Mindset Labs.
  • Got a drum set (thanks Kit) and started to learn drums.
  • Stopped being vegan and started eating fish and a little dairy.
  • Stopped being Straight Edge and began brewing beer. I have finished three batches.
  • Still working on my HCI Masters.
  • Still working on learning python. Now via Google’s Python Class.
  • Got my skin cleared up. Apparently it was rosacea.
  • Moved to FITS at DePaul (lateral career move).
  • Got older. I’m 30 now.

I know it’s not a ton but it’s still something. I’ve also finished some books including Pillars of the Earth, World Without End, Into Thin Air, How to Stop Worrying and Start Living.

I am also considering migrating this blog over to my own install of WP or TXP at flamingballofwreckage.net. I feel like using that domain might be a good thing.

I need to write more about what I’m doing so look for more of that coming up.