Around 20 people joined us for a conversation about Smarter Leaders, which was organized by Jack Mason in the IBM Virtual Analytics Center. Rawn Shah and I gave introductory remarks, and then we facilitated small-group discussions. I focused on the need for smarter leaders at every level and what we could do to help people develop as leaders.
What did I learn?
We know what can help: identifying characteristics of effective leaders, focusing on leadership instead of technology, collecting and sharing success stories, compiling a cookbook that focuses on needs instead of tools… That part is just a matter of doing it, and there are lots of programs already underway.
Is it going to be enough, or are there other things we can do to break through? If it took e-mail ten or so years to become part of the corporate culture and enable all sorts of opportunities, can we wait that long for connected leadership to become part of the way we work?
We tend to have a culture of waiting for permission instead of experimenting (and asking for forgiveness if needed). This means that lots of people are waiting for their managers and executives to participate in this.
Me, I’m all for people taking responsibility for leadership at any level. We might not make big decisions, but we can still make a difference.
What am I going to do based on what I learned?
I’m going to take a look at the characteristics that describe IBMers at their best. I’m going to figure out how to develop those characteristics myself, and how other people can develop them.
I’m passionate about helping individual contributors build and demonstrate leadership. I’m neither a manager nor an executive, and I don’t want to wait for everyone at the top to “get it” before the benefits can trickle down to everyone else. So I’m going to keep poking this idea of leadership until more people can identify with it and ask themselves, “How can I be a smarter leader?”.
What are you going to do to
spread be the word about smarter leaders? =)
What worked well? What could we improve further?
Lots of good stuff!
Pandesal. Brown paper bags of crunchy-yet-soft buns at breakfast and merienda, often accompanied by hot chocolate—or chocolate porridge, if I was really lucky.
If there is a type of bread in my heart, it is this. Not white bread or whole wheat or rye or flax. Not the focaccia we dipped into balsamic vinegar and olive oil at the Italian restaurant my family often went to. Not the banana breads or cornbreads W- and I have made.
Pan de sal. Bread of salt.
Perhaps Laura Esquivel was on to something in Like Water for Chocolate. Food really is a language powerful beyond words.
I made pandesal for the first time. Fresh from the oven, they tasted of home.
I offered W- a piece. He had pandesal during our trips to the Philippines. I was glad he could relate to my memories.
There are places that sell pandesal in Toronto. I’ve never been to them. It’s different. Going out of my way to buy Filipino food? That’s something I might do if I get really homesick. Learning how to make the food of my memories? That fits. That helps me grow.
Picture by W-
From last week’s plans:
Plans for next week:
The world is an explosion of colour. It’s gardening season, and I can’t wait to explore herbs, vegetables, and fruits. It’s biking season, and I’m lovin’ the exercise. As for sewing, I’ve swapped out my fleeces and started sewing linen. Spring has sprung!
I’ve been slowly working on improving little things in life. For example, I picked up a few more porcelain saucers from Goodwill so that I’m not always scrambling to find clean saucers for guests during my tea parties. I’ve also been doing lots and lots of gardening, and the plants are starting to poke up. Lots of drawing, too.
Monthly goal review: One of my goals for April was to figure out how to free up a weekend instead of doing batch cooking all the time. Now we’ve got the roast-chicken-for-frozen-lunches extravaganza down pat, and have a month of frozen goodies ready to go. Yay! I’ve been doing a bit of sewing, too, and have made a few tops I enjoy wearing. I haven’t been doing a lot of Emacs tweaking, though. That’s okay – I’ve been busy doing other things.
What’s up for May? I’m going to focus on slowing down and being mindful. I want to focus on the moment and giving myself time to tidy up, double-check, and reflect. I occasionally misplace or forget things, and I can get better at remembering. So May is spring-cleaning month. I’m going to get the hang of this!
After preparing my personal business commitments in March, I spent some time thinking a lot about career growth. I thought I’d figured out my dreams of wild success, but I suspect there’s still a lot missing from that picture. Here are some thoughts on work:
I’ve been exploring leadership, collaboration, presentations, and creativity:
I’ve been thinking about life, too:
And writing about other stuff!
The squirrels had messed with the wrong seedlings.
To entertain cats and people alike, we’d fed the squirrels throughout winter. Now we were paying for it with the consequences of population explosion: ravaged seedling beds, munched-on sprouts, and dug-up and discarded onion bulbs.
The stench of bloodmeal didn’t stop the marauding rodents from plundering our vegetable patches. We didn’t want to use hot pepper flakes and other painful irritants. We needed a plan.
We stapled chicken wire on our raised beds, which kept the scallions and other bulbs safe for the moment. When the strawberry plants grew tall enough to poke white and pink flowers through the mesh, we knew we needed something bigger.
I was about to experiment with circles of chicken wire held together with duct tape and string. But W- had an engineering decree, and he wasn’t afraid to use it.
After days of discussing diagrams on scratch paper, we decided to build a semi-permanent frame. We picked up spruce and hardware from Home Depot, and then set to work. W- taught me how to use a circular saw to cut the lumber. I told him that it felt a lot like sewing: marking my seams and following the lines. We sanded, measured, marked, leveled, measured, and fastened. We finished the frame just as the sky darkened.
This is what it will probably look like:
We’ve finished the vertical and horizontal supports, and we’ll work on the chicken-wire doors this week. I’m looking forward to it.
I’m making my peace with subjects I detested in school. Now that sewing and cooking have become enjoyable hobbies, I’ve set my sights on shop class and drafting.
Working on shelves and other small projects in high school shop/tech class, I had felt awkward and clumsy. I struggled to wrap my mind around the spatial puzzles of carpentry. The classroom was full of sweat and sawdust, and the lab coats we wore did nothing for either.
Drafting classes in fourth year were more refined, but not more enjoyable. My classmates drew neat lines that intersected at just the right places. My papers were full of smudges, distortions, and impossibilities.
Now, without the pressure of a classroom and with more developed spatial skills (thank you, sewing and drawing), I can find these long-forsaken subjects relaxing, even enjoyable. Working with wood, I found myself thinking of other things I’d like to build. Drawing the structure, I though
Helps to have the right tools, too. Axonometric grids in Inkscape for drawing isometric images? Yes! So much easier than erasing and redrawing segments.
It’s great to challenge my memories. I’m learning that sometimes things are better learned the second time around. It’s great to know it wasn’t me, it was then. Who knows? I may yet revisit the Love Song of J. Alfred Prufrock and learn how to look at fiction and poetry with a critical eye.
But first, I have some squirrels to chase off the lawn.
I was checking out a few things on my blog today, and I came across my WordPress Post Notification administration page. “Hmm,” I said. “I seem to have misconfigured this.” No e-mail had been sent out since August 2009. I figured out that the configuration directory didn’t have write permissions, enabled it, and went on with the rest of my day.
In the evening, I checked my personal mail on my iPod Touch. Inbox…
323 unread messages. That wasn’t right. I read the e-mail subjects. Holy cow, my blog had sent out every single one of my posts in the past half-year.
Granted, the only people on the list had double-opted-in, but still. I’d be annoyed if that many messages showed up in my inbox too, instead of one at a time.
First step: Control the damage. I moved post-notification out of the way, automatically disabling the plugin.
Second: Figure out the impact. 50 e-mail addresses left. Two nasty-notes.
Third: Gingerly re-enable the plugin after removing the locking directory.
Fourth: E-mail everyone an apology.
Fifth: Write about what happened. Tradeoff: Personal embarrassment versus possibility of saving other people from doing this kind of stuff. Worth it.
Looking at the bright side (because there always is a bright side)… At least I’m learning this now instead of later. And with my blog instead of a customer site. And with a smaller list instead of a megafan community. And… umm… it’s e-mail instead of text messages. Which has happened before. I was writing a Perl script that sent messages, and I had a bug, and there was an infinite loop, and poof! there went the balance on my prepaid card.
Dear 8-year-old Sacha,
You might not believe me, but your interest in computers will lead to making lots of good friends. So go ahead, enjoy it, and don't mind the people who tease you about being a geek. When you grow up, this will be a very good thing.
I don't know which experiences I'd want to steer you towards or away from. Even the tough decisions and seeming mistakes turn out to be all right. The boy whose heart you'll break in second year high school will turn out to be a good friend later on. The writer you'll have a crush on (although you'll eventually find conversation awkward because you aren't immersed enough in fiction) will end up telling you about a scholarship that will take you halfway around the world. The big fight you have with a friend at school will make it easier to leave for Japan, where you'll meet the research supervisor who'll convince the department to accept your application. Things work out.
There are a few minor things you might do differently, but don't worry, life works out to be pretty awesome anyway. These suggestions will probably make life better without messing up the space-time continuum too much:
Stick up for yourself, learn and share as much as you can, and enjoy. Life is going to be awesome.
My project for May 2010 is to get better at remembering and decluttering. I want to reclaim the time and energy I spend looking for things. I don't want to inconvenience W-. It feels good to remember and have things in the right place.
Notes for an upcoming presentation on “Remote Presentations That Rock”, for IBM’s “Best of the Technical Leadership Exchange” series. (Whee!) Compare this with the original.
I'll be the first to confess: I've checked mail and surfed the web while "listening" to presentations. I hated not being able to pay attention, but it was hard to concentrate when the speaker was just reading the slides. Whose fault was it? Mine, for being easily distracted? Or the speaker's, for wasting my time?
And sometimes I was the speaker trying to figure out how to be more interesting than e-mail. It's hard!
Chances are, you've been in that situation too, both as listener and as speaker. I want to share with you the top tips I've picked up from years of doing and watching remote presentations. Little things can make such a big difference. I want to convince you to pick one of these tips and use them to make your next presentation rock. Here they are:
Don't be a robot. Make your presentations real. Don't be a recording. Interact. Don't run over time. Make room for learning. Don't do too much. Keep it simple. Don't limit yourself. Practise everywhere. Don't build suspense. Start strong and end strong. Don't stop there. See the big picture of your presentation.
Have you ever listened to speakers who found their own topics boring? Or droned on and on in a monotone? Or who just couldn't keep you interested?
Why do speakers do this? Chances are, it's because the presentation isn't real enough to them. They can't see people's reactions. They can't see people falling asleep. They're trying to squeeze a talk into a busy day. They're distracted by other priorities. They don't have the time or energy to care.
Or sometimes, people are just plain too nervous to relax. They're worried about making mistakes.
You might be thinking: "But Sacha, I have to sound serious! I can't get away with sounding as excited as you!"
You don't have to sound like a used-car salesman or a rabbit on a sugar rush, but you do need to sound alive. You need to really want to connect with people. You can sound serious as long as people know you care about helping them understand.
The basics: It's hard to be energetic if your neck is sore and you can't breathe well. That's the position you often end up in if you don't have a headset for your phone. Do yourself a favour and get yourself a phone headset.
Smile. People will hear that in your voice. Stand up if that helps. Use your hands to gesture, even if no one can see them. Wear your favourite suit if it will give you confidence.
Imagine the people you're talking to, and pretend they're in front of you. Pictures of people can make this easier.
Even better: instead of just sharing your slides, use a webcam to add video. That way, people can see your facial expressions and even your hand gestures.
Don't be a robot. Be real. Make that connection.
Part of being real is interacting. Think about the last time you attended a presentation that didn't have time for questions or interaction. Didn't you wish you could just catch the replay?
Think about the last time you listened to someone reading a script. Didn't you wish you could just get the e-mail instead?
Don't waste people's time. If people are attending your session, it isn't so that they can read your slides - or listen to you reading your slides. They're there because they're interested, they want to ask questions, and they want to learn.
Build interaction into your presentation so that you can find out what's important to people, what they're interested in, what they want to learn more about. If not, you might find that you've just spent an hour talking about topics 1, 2, and 3, when people are still trying to understand topic 1.
How can you build more interaction into your talk? Explore your teleconferences' tools for interaction. For example, I ask people to use the text chat to share their questions and ideas throughout the session. In fact, remote presentations can be more interactive than face-to-face ones, because people don't have to wait for the microphone or a Q&A session.
Many webconferences will let you see how many people have raised their hands. Some even make it easy for people to answer multiple-choice questions or draw on a shared whiteboard. Experiment and explore.
Feel overwhelmed? Ask a buddy to watch the text chat, keep an eye out for raised hands, or set up the urveys for you so that you can focus on speaking.
When you build interaction into your talk, you help people learn, and you learn a lot along the way.
Imagine you're giving a presentation for a lunch-and-learn. You think sixty minutes should be plenty of time. But you lose ten minutes waiting for everyone and dealing with technical troubles. Then someone asks a question, and you spend 5 minutes answering it. You try to get through the rest of the presentation, but you realize that it's already 12:50 and you're nowhere near the end. You flip through your slides quickly, and manage to make it to the end by 1:03. You ask: "Any questions?" but all you hear are the beeps of people dropping from the call so that they can make it to their next meeting.
Virtual conferences are worse, because speakers who take too much time mess up the schedule for everyone else.
This happens in face-to-face presentations, but remote presentations are even more challenging because people usually schedule other things right after your presentation. Back-to-back meetings mean that if you run late, people will miss your key points or the Q&A.
Here's how you can make sure you always end on time: Plan for a much shorter time than you have. Don't try to cram 80 minutes of speaking into 60 minutes. Get your key message across in 10 to 20 minutes, or even shorter. Then plan backup material so that you can take more time if needed.
For example, although this session is supposed to be sixty minutes long, I can give you an executive summary in less than three minutes. I recorded this talk as a 14-minute video. All the rest of the time is for questions and answers, which is where the real value is.
When you have a clear plan, you can make your session longer or shorter as needed. Do you need to keep talking because the next speaker is still missing? Tell more stories. Do you have to do your talk quickly because technical troubles stole twenty minutes? Don't talk faster, just focus on the important points. Be flexible and respect people's time.
What causes people to go over time? It's because they're trying to do too much.
Think about the last time you attended a presentation that tried to cover too many topics. Think about slides that had so much text on them that you couldn't figure out where to start. It doesn't work for you, and it doesn't work for people listening to you.
"But Sacha, I need all those details," you say. Yes, but people can't listen to you, read your slides, and understand everything all at the same time. Make a simpler presentation, then share the details separately.
When I plan a presentation, I focus on one thing I want people to do. Then I think of three to seven things that support that key message. That's it. It's easier to keep things simple when you start small, instead of trying to shoehorn a large presentation into a limited space. If you need to summarize a big presentation, read through everything, then take a step back and say: "What do I want people to do or remember?" Start from there and figure that out before you make a single slide.
Keep it simple in terms of technology, too. Have a simple backup plan just in case. That way, you don't panic when your fancy animations or your technology demo doesn't work. No demo? Use slides. No slides? Talk about your key message. No teleconference? Send an e-mail or reschedule. Keep it simple.
"But Sacha, it takes time to make things simple!" Yes. It takes time to figure out what you want to say and how you want to say it. It's easier to tell people everything you know, instead of the one or two things they need. It's easier to take someone else's deck and hope you can talk your way through it, instead of customizing it to fit what you know.
At the very least, you should read through a deck before presenting it, and you should try out your conference tools before you use them with a real audience.
But you know that already. So here's a useful, unconventional tip: even if you can't spend a few hours working on your presentation, you can still practise while doing other things.
I spend more than eight hours a day working on my presentations. How? When I read or experiment, I learn things that might be useful for a talk. When I talk or write to people, I learn more about what I want to say and how I want to say it. When I watch other people, I take notes on what they're saying AND how they're presenting it. I learn from conversations and commercials. Before big presentations like this one, I even end up rehearsing in my dreams. As I keep talking about something, I figure out my key message and how I can share it.
You might not have time to go to presentation classes or public speaking clubs like Toastmasters, but you have plenty of opportunities to practise. Talk to yourself. Seriously. Your presentations will be much better when you don't just write them, you listen to yourself saying them. For example, you will probably never use the word "utilize" again, because "use" feels much more natural.
Talk to other people about what you're going to present. Write about what you're going to present. Practice isn't just about scripting your talk and re-reading it. You can practise any time, anywhere.
Stand-up comedians practise all the time so that they can figure out their punchlines, and they always keep an eye out for interesting things they can turn into jokes. If you practise, I can't promise that you'll be funny, but you will be much clearer and more confident.
Speaking of stand-up comedians - this is where you shouldn't be like them. When you're telling a joke, it's okay to build up the suspense. When you're giving a remote presentation, don't wait until the end of your talk to say your key message, because you're not going to have the time to do that. Say what you want to say within the first five to ten minutes, then spend the rest of the time explaining the details and handling questions.
"But Sacha, if I do that, everyone's going to leave right away!"
That's terrific! You've just saved everyone time. If you say your key message at the beginning instead of at the end of your talk, then the people who are super-busy can get on with the rest of their day, while the people who need to find out more can stay for questions. Also, by getting your message in early, you'll make it easy for people to remember.
What does this mean for you? Move your executive summary to the front. You can still talk about your agenda and how you're going to talk about things, but put the important stuff first. Start strong.
End strong, too. Let's say that you've made the most of tip #3 and planned for plenty of time for questions and answers. Don't make your last slide show just "Q&A" or "Thank you!". It's a waste of time and space. Instead, make a one-slide summary of the key points and next actions from your talk. Include contact information and a link where people can find out more. Use that one-slide summary as your Q&A slide so that people can remember what they want to ask questions about. It's simple, easy to do, and very effective.
Jumpstart questions and answers by preparing some questions that people usually ask you. If people have been using the text chat throughout your session, you probably have lots of questions to deal with already. Great! Go for it.
Then take back control at the end of the session. Save five minutes at the end so that you can give a quick summary of your talk, the key points from Q&A, and the next actions you want people to take. That way, people's last impressions of your talk are the ones you want them to have.
People remember the beginning and the end more clearly than what's in the middle. Take advantage of that by starting strong and ending strong.
Many people have a hard time doing a strong ending because they don't know what they want people to do next. Have you ever watched a presentation and thought, "Okay, now what do I do?"
When you speak, you need to understand the bigger picture of your presentation. Your presentation never stands by itself. It should lead into something. What do you want people to do? What do you want people to feel? What do you want people to remember? How do you want to change people's minds? Your presentation is not the end. It's the beginning.
For example, after this presentation, I want you to take one of these tips and use it to make your next presentation better. I want you to watch other remote presentations and learn about what they do well and what can be improved. I want you to download the slides and read my article, and I want you to share that with other people. Those are the next steps that this presentation must help you take. The bigger goal I have is to help people make more effective remote presentations (so that I don't have to sit through boring ones!).
Next time you make a presentation, think: What do I want people to do after this? It doesn't matter if you're reporting utilization rates or talking about the technical details of a new product - you still want people to remember something, change something, do something. If you don't, then there's no reason to give a presentation - just send a document.
Seeing the big picture also means you can get a lot more ROI from the time and effort you invest into making a presentation. Using the same work you put into the presentation, you can share slides, handouts, videos, follow-up tips, and many other resources. For example, I gave a presentation to 90 people. When I put the slides up online, they were viewed 24,000 times. 24,000 more views for five minutes of additional work? Yes! It's all part of the bigger picture of a presentation: the conversations that go on after your talk.
In fact, you can get that kind of return even before you make a presentation. For example, when I'm working on a presentation, I tell people I'm working on a presentation. I post my presentation outline on my blog, where people can see it and give suggestions. I post my presentation script as a blog entry. I post my slides. I talk to people about it. As a result, by the time I get to the actual presentation, I've had lots of practice. Remember tip 5 about practicing everyday? This is how you do it. And I also have lots of feedback and lots of connections, all because of these conversations before my talk.
You need to see the big picture of your presentation. Why does your talk matter? What do you want people to do after your talk? How can you keep the conversation going? How can you start the conversation earlier? How can you involve more people? How can you increase your ROI? Plan how, and build that into your presentation.
Don't be a robot. Make your presentations real. Don't be a recording. Interact. Don't run over time. Make room for learning. Don't do too much. Keep it simple. Don't limit yourself. Practise everywhere. Don't build suspense. Start strong and end strong. Don't stop there. See the big picture of your presentation.
You've probably heard tips like these before, but there's a big difference between hearing them and doing them. Focus on one of these tips and use it to make your next presentation better. Watch other remote presentations. Take notes on what they do well and what can be improved. Download these slides or read my notes, and share them with other people.
We spend so many hours in remote presentations, and little things can make such a big difference. Down with boring presentations, and ever onward to remote presentations that rock!
Having your own domain name means being able to use the same e-mail address or website URL for as long as you like.
Here’s how to register a domain name, get it set up for e-mail, and point it to another website. These instructions assume you’re using Namecheap; adjust them if you’re using something else.
When you’re done with the registration process, sign in again if you need to.
If you use Gmail to send mail, you can send mail using your new e-mail address in the From: field. Check out these instructions: Adding a custom ‘From’ address.
You can do more with domain names than what I’ve described here, but that might require some geeking out. =)
That was a very busy weekend. =)
From last week’s plans:
Plans for the week ending May 16, 2010:
Remember these things as you go through life:
Happiness is a state of mind. It can't be bought, given, or taken away, but it can be chosen.
Simple things can make it easier to be happy. Experiment.
Don't forget that the reason you make a safety net is so that you can fly.
Tough experiences make good stories. Everything will work out.
It's possible to have laugh lines, great stories, and lots of energy well into what most people would consider old age. Go for it.
On the different components of happiness: You can manage yourself. You can create opportunities for work. Relationships are different. You can’t control them, but you can invest in them.
Good parenting comes from a strong partnership. Don't let kids or work distract you.
People will flow into and out of your life. Grief is the other face of love. It's worth it. The holes in your life show you what people added and force you to grow.
The world is full of good people.
It's never too late to learn something.
Always give yourself room to write, reflect, draw, and know yourself.
Other people can learn from you, even while you're learning.
As you grow older, people will focus less on your "potential". You won't get as many lucky breaks practically handed to you, but you can keep creating opportunitites. This is good. It gives you the freedom to explore.
You don't have to do everything. Eliminate, negotiate, and delegate.
You are not responsible for everything. Give people room to learn from their own experiments.
Don't let nostalgia for the good old days blind you to the good new days.
Whether or not the world falls apart, life is what you make of it. Worry only so much as that helps you plan. Skip blame entirely. Look for the bright side, and share it with others. Life will be awesome.
I’m working on revising my Remote Presentations That Rock presentation because it’s going to be featured in the “Best of the Technical Leadership Exchange 2009” series at work. (Whee!) Because the content’s already available via video recordings, slides, and blog posts, I’m trying to figure out how to add extra value to the talk so that it’s worth experiencing live.
Timothy Kelpsas reached out to me with a teaser about running multiple sub-plots to help with remote listeners. I finally got to ask him what he meant. Thirty minutes was far too short! =)
Tim thinks of presentations or facilitated sessions like a movie. Just as a director might plant clues about upcoming scenes (foreshadowing) or refer to previous events (flashbacks), Tim plans short forward-looking and backward-looking throughout the session. He establishes a rhythm. And just as a director mixes up action, comedy, romance, and other parts to appeal to different audiences, Tim tries to make sure that different kinds of people get engaged in a variety of activities. He shared how he thinks about introversion and extroversion, multiple intelligences, and other preferences that influence how people learn.
For remote audiences, he keeps a few tips in mind:
How can I apply what I’m learning?
Instead of repeating the same presentation, I’m going to revise it thoroughly. I know the core ideas are sound. Not only did the content get me voted into the Best of the TLE series, but lots of people have reused it already, and people tell me that the tips are very useful. Now I get to experiment with more effective ways to present those tips. Taking Barclay Brown’s suggestion to use the basic fiction plots, I’m going to revise it to use a revenge plot. (Now I’m curious about how I might pull that off, and if I’m curious, chances are other people will be curious too!) I think that will be more fun than the quest plot, and the more vivid I can make things, the more people might remember.
Building on that subplot, I can weave reflection through more of the presentation. The original presentation had a little bit of reflection up front, but the revenge plot gives me plenty of opportunities to build reflection in.
Applying Tim’s tips, I’m going to prompt myself to “break the fourth wall” and address people listening to the replay. I generally haven’t done this because I prepare blog posts, slides, and the occasional short standalone video for replay audiences, but people might come in through the web conference archives and miss out on the additional resources. Besides, it must be possible to do a good live/replay mix, and the practice will help me with in-person presentations as well.
Also awesome: The very first thing Tim did when we connected on the phone was to sing to me. He explained afterwards that he wanted to make sure I quickly got the sense of who he was. You bet that’s sticking in my memory! I told Tim how it reminded me of when Ethan McCarty sang me a song when I dropped by IBM NY. Ah, IBM and awesome people having fun… =)
The search-and-replace tokens are defined in the first row of your Microsoft Excel spreadsheet. The script searches for them in the message body, replaces them with the appropriate values from the current row, and either saves the message as the draft or sends the message. There are a few built-in tokens for this script ([to], [cc], [subject]) - these are case-sensitive, so enter them exactly like that. All the other tokens are up to you, so you could use FOO and BAR as search-and-replace tokens if you want.
Tokens are replaced only in the message body. If you want a variable subject line, use a formula to calculate the subject in a column with the [subject] header.As always, test your mail merges with a small list before using it for your entire list. Create an agent and call it something like "Mail merge". Edit the agent and set the type to LotusScript. In the (Declarations) section, add
%Include "lsconst.lss"In the "Initialize" section, put in:
Sub Initialize 'Mail merge script by Sacha Chua (firstname.lastname@example.org) Dim ws As NotesUIWorkspace Set ws = New NotesUIWorkspace Dim sendTypes(1) As String Dim sendValue As String Dim errorCount As Integer errorCount = 0 sendTypes(0) = "Draft messages without sending" sendTypes(1) = "Send messages" sendValue = ws.Prompt(PROMPT_OKCANCELLIST, "Sending options", "What would you like to do?", "", sendTypes) If (sendValue = "") Then Exit Sub End If Dim fileName As String Dim strXLFilename As String 'Prompt for the filename - should be a Microsoft Excel file with columns, where the first row of each column 'is a token that will be used when replacing text in the body of the message 'Special tokens: [to], [cc], [subject] set the appropriate fields 'Make sure the first column does not have any blank cells fileName$ = ws.Prompt(12, "Select file", "3") If fileName$ = "" Then Exit Sub 'Cancel was pressed End If strXLFilename = fileName$ Dim s As New NotesSession Dim uidoc As NotesUIDocument Dim partno As String Dim db As NotesDatabase Dim view As NotesView Dim doc As NotesDocument Dim collection As NotesDocumentCollection Dim memo As NotesDocument Dim body As NotesRichTextItem Dim newBody As NotesRichTextItem Dim range As NotesRichTextRange Dim count As Integer Set db = s.CurrentDatabase Set collection = db.UnprocessedDocuments Set memo = collection.getFirstDocument() Dim varXLFile As variant 'Get data from the spreadsheet Set varXLFile = CreateObject("Excel.Application") varXLFile.Visible = False Dim varXLWorkbook As variant Set varXLWorkbook = Nothing varXLFile.Workbooks.Open strXLFilename Set varXLWorkbook = varXLFile.ActiveWorkbook Dim varXLSheet As variant Set varXLSheet = varXLWorkbook.ActiveSheet Dim lngRow As Integer Dim columnNo As Integer Dim token As String Dim value As string lngRow = 2 Dim maildoc As NotesDocument While (Not (varXLSheet.Cells(lngRow, 1).Value = "")) 'Fill in the template Dim subject As string subject = memo.Subject(0) Set body = memo.GetFirstItem("Body") 'Compose message Set maildoc = New NotesDocument(db) Set maildoc= db.CreateDocument() maildoc.Form = "Memo" maildoc.Subject = subject Set newBody = maildoc.CreateRichTextItem("Body") Call newBody.appendRTItem(body) Set range = newBody.CreateRange 'Count the number of fields 'Look up tokens from the column headings and replace them columnNo = 1 While Not(varXLSheet.Cells(1, columnNo).Value = "") token = varXLSheet.Cells(1, columnNo).Value value = varXLSheet.Cells(lngRow, columnNo).Value count = range.FindAndReplace(token, value, 16) If (token = "[to]") Then maildoc.SendTo = value End If If (token = "[cc]") Then maildoc.CopyTo = value End If If (token = "[subject]") Then maildoc.Subject = value End If columnNo = columnNo + 1 Wend On Error GoTo save If (sendValue = sendTypes(0)) Then Call maildoc.Save(True, False) Else maildoc.SaveMessageOnSend = True maildoc.PostedDate = Now() Call maildoc.Send(False) Call maildoc.Save(True, True) End If GoTo nextrow save: MessageBox("Error processing " + maildoc.sendTo) errorCount = errorCount + 1 Resume Next nextrow: lngRow = lngRow + 1 Wend If (sendValue = sendTypes(0)) Then MsgBox "Drafted " & (lngRow - errorCount - 2) & " message(s). Errors: " & errorCount Else MsgBox "Sent " & (lngRow - errorCount - 2) & " message(s). Errors: " & errorCount End If Call varXLFile.Quit() End Sub
Some people have one hobby at a time. I tend to have several that shift over time, and I’ve gone through a lot of interests.
I started sketching my current hobbies to get a sense of how woodworking might fit in, and how it interacts with my other interests. I wanted to figure out if I could explore that interest enough to reach the point of being able to comfortably do it. In particular, woodworking tends to require blocks of unstructured time, which means it competes with sewing and cooking for those precious weekend blocks.
I got interested in woodworking because of gardening, and there are a number of items we can build to make gardening easier. Now that we’ve set the garden up, it doesn’t take that much more time: ten minutes each day, and maybe an hour during weekends to turn the compost and take care of additional tasks.
Woodworking conflicts with cooking, though, because we use the kitchen and there’s no point in cooking when sawdust is floating around. That’s okay. We’re at a “good enough” level in cooking, and batch-cooking lets us free up weekends.
Drawing helps a lot with woodworking, and woodworking helps me develop spatial intelligence for better drawing.
Looking at my other hobbies, you can see that writing, drawing, and presentations all feed each other. I’m not making as good a use of photography as I could, so that’s something to develop. All the hobbies that I actively work on are well-connected. In contrast, something like music doesn’t connect well with my other interests, so I rarely end up practising on the piano.
Picking hobbies that fit together means that you get more value for the time and energy you put in. The more you develop skills in one area, the more effectively you can do connected areas.
Another related post: How to do a lot
I finally got around to asking my manager for permission to contribute org-toodledo as open source. Here it is. Enjoy!
;;; org-toodledo.el - Toodledo integration for Emacs Org mode ;; (c) 2010 Sacha Chua (email@example.com) ;; ;; This file is not part of GNU Emacs. ;; This is free software; you can redistribute it and/or modify it under ;; the terms of the GNU General Public License as published by the Free ;; Software Foundation; either version 2, or (at your option) any later ;; version. ;; ;; This is distributed in the hope that it will be useful, but WITHOUT ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ;; for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, ;; MA 02111-1307, USA. ;; How to use: ;; 1. Customize org-toodledo-userid and org-toodledo-password ;; 2. Open a blank org file. ;; 3. Call org-toodledo-initialize-org ;; Call org-toodledo-update to bring in new/updated tasks (skips locally modified tasks newer than updated) ;; Call org-toodledo-sync-task to create or update the current task ;; Call org-toodledo-delete-current-task to delete the current task ;; ;; Doesn't do lots of error trapping. Might be a good idea to version-control your Org file. ;; ;; TOODLEDO ATTRIBUTES and how they are bi-directionally handled ;; Context: Handled by tags (ex: :@work: :@errands:) ;; - will create new contexts if necessary ;; Task status: Mapped to TODO state. ;; See org-toodledo-status-to-string and org-toodledo-parse-current-task for the mapping ;; You will probably want something like this in your ~/.emacs: ;; (setq org-todo-keywords ;; '((sequence ;; "TODO(t)" ; next action ;; "PLAN(-)" ;; "STARTED(s)" ;; "WAITING(w@/!)" ;; "POSTPONED(p)" "SOMEDAY(s@/!)" "|" "DONE(x!)" "CANCELLED(c@)") ;; (type "DELEGATED(d@!)" "DONE(x)"))) ;; Length: Mapped to effort ;; Priority: Mapped to [#A], [#B], or [#C]. (TODO: Change this to five levels of priority to match Toodledo) ;; Start date: Mapped to "SCHEDULED" ;; Due date: Mapped to "DEADLINE" ;; Tags: Mapped to tags ;; Note: Mapped to todo text. May get confused by asterisks, so don't use any starting asterisks in your body text. ;; (or anything that looks like an Org headline). ;; Completed: Mapped to DONE todo state. ;; ;; TODO: ;; - [ ] Double-check new/changed/deleted task updating, still seems buggy ;; - [ ] Test, test, test - maybe make test harness? ;; - [ ] Move status<->string mapping to a variable - lookups are better than logic ;; - [ ] Make sure sync timestamps aren't getting updated more often than needed ;; - [ ] Suggest some kind of hook to make it easier to mark a task as locally modified (require 'org) (require 'w3m) (require 'xml) (defcustom org-toodledo-userid "" "UserID from Toodledo: http://www.toodledo.com/info/api_doc.php" :group 'org-toodledo :type 'string) (defcustom org-toodledo-password "" "Password for Toodledo." :group 'org-toodledo :type 'string) (defvar org-toodledo-token-expiry nil "Expiry time for authentication token.") (defvar org-toodledo-token nil "Authentication token.") (defvar org-toodledo-key nil "Authentication key.") (require 'url) (require 'url-http) (defun org-toodledo-initialize-org () "Replace buffer contents with Toodledo tasks." (interactive) (delete-region (point-min) (point-max)) (let ((account-info (org-toodledo-get-account-info)) (server-info (org-toodledo-get-server-info)) (tasks (org-toodledo-get-tasks '(("notcomp" . "1"))))) (insert "* Toodledo\n" ":PROPERTIES:\n" ":Last-modified: " (cdr (assoc "lastaddedit" account-info)) "\n" ":Last-deleted: " (cdr (assoc "lastdelete" account-info)) "\n" ":Last-sync: " (cdr (assoc "unixtime" server-info)) "\n" ":END:\n") (insert (mapconcat 'org-toodledo-task-to-string tasks "\n")))) (defun org-toodledo-get-token () "Retrieve authentication token valid for four hours." (if (and org-toodledo-token org-toodledo-token-expiry (time-less-p (current-time) org-toodledo-token-expiry)) org-toodledo-token ;; Else retrieve a new token (let ((response (with-current-buffer (url-retrieve-synchronously (concat "http://api.toodledo.com/api.php?method=getToken;userid=" org-toodledo-userid)) (xml-parse-region (point-min) (point-max))))) (if (equal (car (car response)) 'error) (progn (setq org-toodledo-token nil org-toodledo-key nil org-toodledo-token-expiry nil) (error "Could not log in to Toodledo: %s" (elt (car response) 2))) (setq org-toodledo-token (elt (car response) 2)) (setq org-toodledo-key (org-toodledo-key) ;; Set the expiry time org-toodledo-token-expiry (seconds-to-time (+ (time-to-seconds (current-time)) (* 60 60 4))))) ;; four hours org-toodledo-token))) (defun org-toodledo-key () "Return authentication key used for each request." (if (and org-toodledo-token org-toodledo-token-expiry (time-less-p (current-time) org-toodledo-token-expiry) org-toodledo-key) org-toodledo-key (setq org-toodledo-key (md5 (concat (md5 org-toodledo-password) org-toodledo-token org-toodledo-userid))))) (defun org-toodledo-get-url (method-name &optional params) "Return URL for METHOD-NAME and PARAMS." (org-toodledo-get-token) (concat "http://api.toodledo.com/api.php?method=" (w3m-url-encode-string method-name) ";key=" (org-toodledo-key) (if params (concat ";" (mapconcat (lambda (x) (concat (w3m-url-encode-string (car x)) "=" (w3m-url-encode-string (cdr x)))) params ";")) ""))) (defun org-toodledo-call-method (method-name &optional params) "Call METHOD-NAME with PARAMS and return the parsed XML." (setq params (cons (cons "unix" "1") params)) (with-current-buffer (url-retrieve-synchronously (org-toodledo-get-url method-name params)) (xml-parse-region (point-min) (point-max)))) (defmacro org-toodledo-defun (function-name api-name description) `(defun ,function-name (params) ,description (org-toodledo-call-method ,api-name params))) (defun org-toodledo-get-server-info () "Return server information." (org-toodledo-convert-xml-result-to-alist (car (org-toodledo-call-method "getServerInfo")))) (defun org-toodledo-get-account-info () "Return server information." (org-toodledo-convert-xml-result-to-alist (car (org-toodledo-call-method "getAccountInfo")))) (org-toodledo-defun org-toodledo-add-task "addTask" "Add task with PARAMS.") (org-toodledo-defun org-toodledo-edit-task "editTask" "Edit task with PARAMS.") (org-toodledo-defun org-toodledo-delete-task "deleteTask" "Delete task with PARAMS.") ;; (setq temp (org-toodledo-get-tasks '(("notcomp" . "1")))) ;; (setq server-info (org-toodledo-get-server-info)) ;; (setq account-info (org-toodledo-get-account-info)) (defun org-toodledo-convert-xml-result-to-alist (info) "Convert INFO to an alist." (delq nil (mapcar (lambda (item) (if (listp item) (cons (symbol-name (car item)) (elt item 2)))) (xml-node-children (delete "\n\t" info))))) (defun org-toodledo-get-tasks (&optional params) "Retrieve tasks using PARAMS. Return a list of task alists." (mapcar 'org-toodledo-convert-xml-result-to-alist (xml-get-children (car (org-toodledo-call-method "getTasks" params)) 'task))) (defun org-toodledo-get-deleted (&optional params) "Retrieve deleted tasks using PARAMS. Return a list of task alists." (mapcar 'org-toodledo-convert-xml-result-to-alist (xml-get-children (car (org-toodledo-call-method "getDeleted" params)) 'task))) (defun org-toodledo-entry-note () "Extract the note for this entry." (save-excursion (org-back-to-heading) (when (looking-at org-complex-heading-regexp) (goto-char (match-end 0)) (let ((text (buffer-substring-no-properties (point) (if (re-search-forward org-complex-heading-regexp nil t) (match-beginning 0) (org-end-of-subtree))))) (with-temp-buffer (insert text) (goto-char (point-min)) (when (re-search-forward (concat "\\<" (regexp-quote org-deadline-string) " +<[^>\n]+>[ \t]*") nil t) (replace-match "")) (goto-char (point-min)) (when (re-search-forward (concat "\\<" (regexp-quote org-scheduled-string) " +<[^>\n]+>[ \t]*") nil t) (replace-match "")) (goto-char (point-min)) (while (re-search-forward "\n\n+" nil t) (replace-match "\n")) (org-export-remove-or-extract-drawers org-drawers nil nil) (buffer-substring-no-properties (point-min) (point-max))))))) (defun org-toodledo-parse-current-task () "Extract the status and Toodledo ID of the current task." (save-excursion (org-back-to-heading t) (when (and (looking-at org-complex-heading-regexp) (match-string 2)) ;; TODO (let* (info (status (match-string-no-properties 2)) (priority (match-string-no-properties 3)) (title (match-string-no-properties 4)) (tags (match-string-no-properties 5)) (id (org-entry-get (point) "Toodledo-ID")) (contexts (org-toodledo-get-contexts)) context) ;; (add-to-list 'info (cons "title" (match-string-no-properties 1))) (if id (add-to-list 'info (cons "id" id))) (when tags (setq tags (delq nil (mapcar (lambda (tag) (if (> (length tag) 0) (if (string-match (org-re "@\\([[:alnum:]_]+\\)") tag) (progn ;; Not recognized context (if (null (assoc (match-string 1 tag) contexts)) ;; Create it if it does not yet exist (let ((result (org-toodledo-call-method "addContext" (list (cons "title" (match-string 1 tag)))))) (if (eq (caar result) 'added) (setq org-toodledo-contexts (cons (cons (match-string 1 tag) (elt (car result) 2)) org-toodledo-contexts) contexts org-toodledo-contexts)))) ;; Get the ID of the context (setq context (cdr (assoc (match-string 1 tag) contexts))) nil) tag))) (split-string tags ":"))))) (setq info (list (cons "id" id) (cons "title" title) (cons "length" (org-entry-get (point) "Effort")) (cons "context" context) (cons "tag" (mapconcat 'identity tags " ")) (cons "completed" (if (equal status "DONE") "1" "0")) (cons "status" (cond ((equal status "STARTED") "2") ((equal status "DELEGATED") "4") ((equal status "SOMEDAY") "8") ((equal status "CANCELLED") "9") ((equal status "PLAN") "3") ((equal status "WAITING") "5") ((equal status "TODO") "1"))) (cons "priority" (cond ((equal priority "[#A]") "2") ((equal priority "[#B]") "1") ((equal priority "[#C]") "0"))) (cons "note" (org-toodledo-entry-note)))) (when (org-entry-get nil "DEADLINE") (setq info (cons (cons "duedate" (substring (org-entry-get nil "DEADLINE") 0 10)) info))) (when (org-entry-get nil "SCHEDULED") (setq info (cons (cons "startdate" (substring (org-entry-get nil "SCHEDULED") 0 10)) info))) info)))) (defun org-toodledo-sync () "Synchronize all tasks." ;; Retrieve all tasks ;; For each task in the current buffer ;; Synchronize an existing task that has changed (let ((regexp (concat "^\\*+[ \t]+\\(" org-todo-regexp "\\)"))) (goto-char (point-min)) (while (re-search-forward regexp nil t) (org-toodledo-sync-task)))) (defun org-toodledo-update () "Insert new tasks and update previous tasks." (interactive) (let* ((server-info (org-toodledo-get-server-info)) (account-info (org-toodledo-get-account-info)) (changed (org-toodledo-account-changed account-info)) (last-deleted (string-to-number (or (org-entry-get-with-inheritance "Last-deleted") "0"))) (last-modified (string-to-number (or (org-entry-get-with-inheritance "Last-modified") "0"))) (last-update (string-to-number (or (org-entry-get-with-inheritance "Last-sync") "0"))) processed) ;; If tasks have been deleted or modified, then the Toodledo API ;; will give us the timestamps. We need to find out which tasks ;; have been deleted or modified since the last time we retrieved ;; the list of tasks that have been deleted or modified. We store ;; the last times in the properties of the root element. (if (and (assoc "deleted" changed) ;; Tasks have been deleted (>= (string-to-number (cdr (assoc "deleted" changed))) last-deleted)) (setq processed (append (org-toodledo-process-deleted-tasks last-deleted) processed))) (if (and (assoc "modified" changed) ;; Tasks have been added or edited (>= (string-to-number (cdr (assoc "modified" changed))) last-modified)) ;; Retrieve added/modified tasks (setq processed (append (org-toodledo-process-modified-tasks last-modified) processed))) ;; TODO Look for tasks that were modified locally since the last synchronization (org-toodledo-process-locally-modified-tasks last-update processed) ;; TODO Update timestamps here (goto-char (point-min)) (when (re-search-forward (concat "^\\(" outline-regexp "\\)") nil t) (org-entry-put (point) "Last-sync" (cdr (assoc "unixtime" server-info))) (when (assoc "lastaddedit" account-info) (org-entry-put (point) "Last-modified" (cdr (assoc "lastaddedit" account-info)))) (when (assoc "lastdelete" account-info) (org-entry-put (point) "Last-deleted" (cdr (assoc "lastdelete" account-info))))))) (defun org-toodledo-process-locally-modified-tasks (last-update processed) "Synchronize tasks that were locally modified after LAST-UPDATE. Skip tasks with IDs in PROCESSED." (goto-char (point-min)) (let ((start (float-time (current-time)))) (while (re-search-forward org-complex-heading-regexp nil t) ;; Look for all tasks in this buffer (if (match-string 2) ;; Is it a new task, or has it been modified since the last update? (let ((id (org-entry-get (point) "Toodledo-ID")) (modified (string-to-number (or (org-entry-get (point) "Modified") ""))) (last-sync (if (org-entry-get (point) "Sync") (string-to-number (org-entry-get (point) "Sync")) 0))) (if (or (null id) (and (> modified last-sync) (< modified start) (not (member id processed)))) (save-excursion (org-toodledo-sync-task)))))))) (defun org-toodledo-touch () "Update the current task." (interactive) (org-entry-put (point) "Modified" (format "%d" (float-time (current-time))))) (defvar org-toodledo-actually-delete t) (defun org-toodledo-process-deleted-tasks (timestamp) "Remove tasks deleted after TIMESTAMP." (delq nil (mapcar (lambda (task) (when (org-toodledo-find-task task) (if org-toodledo-actually-delete (delete-region (org-back-to-heading) (if (re-search-forward org-complex-heading-regexp nil t) (match-beginning 0) (org-end-of-subtree))) (org-entry-delete (point) "Toodledo-ID") (org-entry-put (point) "Toodledo-Deleted" (timestamp))) (org-toodledo-task-id task))) (org-toodledo-get-deleted (list (cons "after" (number-to-string timestamp))))))) (defun org-toodledo-process-modified-tasks (modified) "Handle all the tasks that have been modified since MODIFIED." (delq nil (mapcar (lambda (task) (if (org-toodledo-find-task task) (if (null (org-toodledo-update-task task modified)) (org-toodledo-task-id task)) (org-toodledo-create-task task))) (org-toodledo-get-tasks (list (cons "modafter" (number-to-string modified))))))) (defun org-toodledo-create-task (task) "Create a task for TASK." (goto-char (point-max)) (if (point-at-eol) (insert "\n")) (insert (org-toodledo-task-to-string task)) (org-toodledo-task-id task)) (defun org-toodledo-find-task (task) "Find the task specified by TASK." (goto-char (point-min)) (re-search-forward (concat "^[ \t]*:Toodledo-ID:[ \t]+" (org-toodledo-task-id task) "$") nil t)) (defun org-toodledo-account-changed (account-info) "Return non-nil if the account has changed since the last check. The result will be an alist of (\"modified\" . \"timestamp\") if tasks have been added/edited and (\"deleted\" . \"timestamp\") if tasks have been deleted." (let ((last-modified (org-entry-get-with-inheritance "Last-modified")) (last-deleted (org-entry-get-with-inheritance "Last-deleted")) result) (if (> (string-to-number (or (cdr (assoc "lastaddedit" account-info)) "0")) (string-to-number (or last-modified "0"))) (add-to-list 'result (cons "modified" last-modified))) (if (> (string-to-number (or (cdr (assoc "lastdelete" account-info)) "")) (string-to-number (or last-deleted "0"))) (add-to-list 'result (cons "deleted" last-deleted))) result)) (defun org-toodledo-sync-task (&optional force) "Update my Toodledo for the current task." (interactive "P") (save-excursion (let ((task (org-toodledo-parse-current-task))) (if (null (org-toodledo-task-id task)) ;; New task, create it (let ((result (org-toodledo-add-task task))) (when (eq (elt (car result) 0) 'added) (org-entry-put (point) "Toodledo-ID" (elt (car result) 2)) (org-entry-put (point) "Sync" (format "%d" (float-time (current-time)) 1000)))) ;; Old task, update (when (org-toodledo-success-p (org-toodledo-edit-task task)) (if (equal (org-toodledo-task-completed task) "1") (org-entry-put (point) "Completed" "1") (org-entry-put (point) "Status" (org-toodledo-task-status task))) (org-entry-put (point) "Sync" (format "%d" (float-time (current-time)) 1000))))))) ;; (assert (equal (org-toodledo-format-date "2003-08-12") "<2003-08-12 Tue>")) (defun org-toodledo-format-date (date &optional repeat) "Return yyyy-mm-dd day for DATE." (concat "<" (format-time-string "%Y-%m-%d %a" (cond ((listp date) date) ((numberp date) (seconds-to-time date)) ((and (stringp date) (string-match "^[0-9]+$" date)) (seconds-to-time (string-to-number date))) (t (apply 'encode-time (org-parse-time-string date))))) (if repeat (concat " " repeat) "") ">")) ;; (mapconcat 'org-toodledo-task-to-string temp "\n") ;; (setq task (elt temp 2)) ;; (org-toodledo-task-to-string task) (defun org-toodledo-task-to-string (task &optional level) "Return an Org-formatted version of TASK." (let* ((repeat (string-to-number (org-toodledo-task-repeat task))) (rep-advanced (org-toodledo-task-repeat-advanced task)) (repeat-string (org-toodledo-repeat-to-string repeat rep-advanced)) (priority (org-toodledo-task-priority task))) (concat (make-string (or level 2) ?*) " " (org-toodledo-status-to-string task) " " (cond ((equal priority "-1") "") ((equal priority "0") "[#C] ") ((equal priority "1") "[#B] ") ((equal priority "2") "[#A] ") ((equal priority "3") "[#A] ")) (org-toodledo-task-title task) (if (org-toodledo-task-context task) (concat " :@" (org-toodledo-task-context task) ":") "") "\n" (if (and (org-toodledo-task-duedate task) (not (equal (org-toodledo-task-duedate task) "")) (not (< (string-to-number (org-toodledo-task-duedate task)) 0))) (concat org-deadline-string " " (org-toodledo-format-date (org-toodledo-task-duedate task) repeat-string) "\n") "") (or (org-toodledo-task-note task) "") "\n" ":PROPERTIES:\n" ":Toodledo-ID: " (org-toodledo-task-id task) "\n" ":Modified: " (org-toodledo-task-modified task) "\n" ":Sync: " (format "%d" (float-time (current-time))) "\n" ":Effort: " (org-toodledo-task-length task) "\n" ":END:\n" ))) ;; (assert (equal (org-toodledo-repeat-to-string 0) "")) ;; (assert (equal (org-toodledo-repeat-to-string 1) "+1w")) ;; (assert (equal (org-toodledo-repeat-to-string 2) "+1m")) ;; (assert (equal (org-toodledo-repeat-to-string 3) "+1y")) ;; (assert (equal (org-toodledo-repeat-to-string 4) "+1d")) ;; (assert (equal (org-toodledo-repeat-to-string 5) "+2w")) ;; (assert (equal (org-toodledo-repeat-to-string 6) "+2m")) ;; (assert (equal (org-toodledo-repeat-to-string 7) "+6m")) ;; (assert (equal (org-toodledo-repeat-to-string 8) "+3m")) ;; (assert (equal (org-toodledo-repeat-to-string 108) ".+3m")) ;; (assert (equal (org-toodledo-repeat-to-string 101) ".+1w")) ;; (assert (equal (org-toodledo-repeat-to-string 0) "")) (defconst org-toodledo-repeat-intervals '("" "+1w" "+1m" "+1y" "+1d" "+2w" "+2m" "+6m" "+3m")) (defun org-toodledo-status-to-string (task) (let ((comp (org-toodledo-task-completed task)) (status (string-to-number (org-toodledo-task-status task)))) (cond ((not (or (null comp) (equal comp "") (equal comp "0"))) "DONE") ((= status 0) "TODO") ((= status 1) "TODO") ((= status 2) "STARTED") ((= status 3) "PLAN") ((= status 4) "DELEGATED") ((= status 5) "WAITING") ((= status 6) "PLAN") ; hold ((= status 7) "SOMEDAY") ; postponed ((= status 8) "SOMEDAY") ((= status 9) "CANCELLED") ))) (defun org-toodledo-repeat-to-string (repeat &optional rep-advanced) "Turn TASK into a repeat sequence." (cond ((= repeat 0) nil) ((> repeat 100) (concat "+" (org-toodledo-repeat-to-string (mod repeat 100) rep-advanced))) ((and (= repeat 50) rep-advanced) (cond ((string-match "Every \\([0-9]+\\) week" rep-advanced) (concat "+" (match-string 1 rep-advanced) "w")) ((string-match "Every \\([0-9]+\\) month" rep-advanced) (concat "+" (match-string 1 rep-advanced) "m")) ((string-match "Every \\([0-9]+\\) year" rep-advanced) (concat "+" (match-string 1 rep-advanced) "y")) ((string-match "Every \\([0-9]+\\) day" rep-advanced) (concat "+" (match-string 1 rep-advanced) "d")) (t rep-advanced))) (t (elt org-toodledo-repeat-intervals repeat)))) (defun org-toodledo-delete-current-task () "Delete the current task." (interactive) (org-back-to-heading t) (let ((task (org-toodledo-parse-current-task))) (and (> (length (org-toodledo-task-id task)) 0) (org-toodledo-success-p (org-toodledo-delete-task task))) (delete-region (point) (if (and (end-of-line) (re-search-forward org-complex-heading-regexp nil t)) (match-beginning 0) (org-end-of-subtree t t) (point))))) (defun org-toodledo-task-get-prop (task prop) (cdr (assoc prop task))) (defmacro org-toodledo-task-prop-defun (field) `(defun ,(intern (concat "org-toodledo-task-" field)) (task) (cdr (assoc ,field task)))) (defun org-toodledo-success-p (result) "Return non-nil if RESULT indicates success." (eq (car (car result)) 'success)) (org-toodledo-task-prop-defun "id") (org-toodledo-task-prop-defun "title") (org-toodledo-task-prop-defun "status") (org-toodledo-task-prop-defun "completed") (org-toodledo-task-prop-defun "repeat") (org-toodledo-task-prop-defun "context") (org-toodledo-task-prop-defun "duedate") (org-toodledo-task-prop-defun "modified") (org-toodledo-task-prop-defun "priority") (org-toodledo-task-prop-defun "note") (org-toodledo-task-prop-defun "length") ;; defun'd separately because of the change in name (defun org-toodledo-task-repeat-advanced (task) (cdr (assoc "rep_advanced" task))) (defvar org-toodledo-contexts nil "An alist of (context . id).") (defun org-toodledo-get-contexts (&optional force) "Store an alist of (context . id) in `org-toodledo-contexts'. Reload if FORCE is non-nil." (if (or force (null org-toodledo-contexts)) (setq org-toodledo-contexts (mapcar (lambda (node) (cons (car (xml-node-children node)) (xml-get-attribute node 'id))) (xml-get-children (car (org-toodledo-call-method "getContexts")) 'context))) org-toodledo-contexts)) (defun org-toodledo-agenda-touch () "Update the Modified timestamp for the current entry in the agenda." (org-agenda-check-type t 'agenda 'timeline) (org-agenda-check-no-diary) (let* ((marker (or (org-get-at-bol 'org-marker) (org-agenda-error))) (buffer (marker-buffer marker)) (pos (marker-position marker))) (org-with-remote-undo buffer (with-current-buffer buffer (widen) (goto-char pos) (if (org-entry-get (point) "Modified") (org-entry-put (point) "Modified" (format "%d" (float-time (current-time))))))))) (defun org-toodledo-update-task (task &optional last-update) (let* ((modified (string-to-number (or (org-entry-get (point) "Modified") ""))) (last-sync (if (org-entry-get (point) "Sync") (string-to-number (org-entry-get (point) "Sync")) 0)) (level (car (org-heading-components))) (locally-modified (> modified last-sync))) ;; Locally modified? keep (if locally-modified nil ;; Not locally modified? replace ;; Figure out what our level is (delete-region (org-back-to-heading) (progn (goto-char (match-end 0)) (if (re-search-forward org-complex-heading-regexp nil t) (goto-char (match-beginning 0)) (org-end-of-subtree)))) (insert (org-toodledo-task-to-string task level)) t))) (provide 'org-toodledo)
Plans from last week:
Plans for next week
It took me a few hours to figure out that I needed the SUMMARY field flag, so I thought I’d save you the struggle if you ever need to create a view with custom columns.
I was trying to create a view that showed the custom fields I’d added to an RSVP message: RSVPCampaign, RSVPCode, and other details. I had created a Lotus Notes button that sent me an e-mail with the information in hidden fields. I successfully processed the extra fields using a LotusScript agent. I wanted to create a view that showed the results so that I didn’t have to keep clicking on the button and checking my mail. Using a view would make it easier for me to share these tools with other people, because I figured out how to create a button that creates a view.
I couldn’t figure out how to get my custom fields to display, though. They showed up in @DocFields, but @IsAvailable(RSVPCampaign) was always false.
I read about all of the functions in the Formula language. I experimented with @GetField, field names, and other ways to access data. I dug through documentation and websites (most of which assumed people already knew things like this). Frustrated, I opened the properties dialog and started systematically going through the fields on my RSVP messages.
It took me several passes to notice that the regular fields had “Field Flags: SUMMARY” and my custom fields didn’t.
After some quick searching, I realized that I needed to set the summary field flag. So I rewrote my RSVP mailing function to include lines like this:
Dim item As NotesItem
Set item = New NotesItem(doc, "RSVPCode", code) item.IsSummary = True
I tested my new RSVP mailing button, and my view worked!
So if you’re stumped because your custom fields don’t work in view selection formulas or in column formulas, set the summary flag on the field when you set the field, and you should be good to go.
I wonder how I could’ve learned that faster. Reading other people’s source code would help. Forums are useful, too. Part of it involves picking up the jargon so that I know how to phrase my searches. Anyway, I fixed it! =D
Yes, I know, I said I wasn’t going to overthink this career thing. During a bike ride to work, I thought it might be a good idea to graph the different things I’m considering in the medium/long-term, how they relate to my comfort zone, and what kind of growth I see ahead.
Development: I understand this path the most. I have a deep background in it, I know I love doing it, I can list some things I want to learn, and I’m already in a good position to work on projects like that. This path is mainly limited by the movement of development to lower-cost areas, but I have a lot of role models who are IT architects or senior developers, so I can imagine what growth looks like.
Consulting: I’ve done a bit of consulting in the past, and I can continue to develop industry knowledge and learn more about frameworks. I can find opportunities to do this, although it’s not as easy as finding development opportunities.
My current role in Innovation Discovery: I’ve gotten the hang of the routine things I need to do, and push outside my comfort zone by creating new tools and resources to help us work. It doesn’t feel as deep in terms of growth as the other paths do, though.
Supporting and leading workshops might be a good thing to grow into. It’s a relatively big jump from where I am and I may need to have a lot more experience (people who do this are several pay-levels above me!). It’s a subset of consulting. It will give me lighter cross-industry knowledge instead of the deeper industry knowledge that comes from extended engagements.
Communications: I know a little about communications, and I enjoy writing and presenting. I don’t know enough about full-time communications work to get a good sense of whether it would be a good fit, though.
Sales: I don’t have any sales experience. It’s a great life and business skill, though, so I’d love to explore it and learn more about client needs, our offerings, and how to match-make the two. What would I bring to the table? I’m good at finding resources/experts.
Management: I don’t have any management experience, although my experiments with virtual assistance helped me learn about delegation. I don’t know yet whether I’d like this a lot or not. One way to find out is to grow in development until I can become a project manager, and then use that project management experience to figure out if I like contributing primarily through a team (instead of as an individual contributor).
It’s good to have a Plan B, C, D, etc. In terms of flexibility and transferable skills, development, consulting, and sales are great. Communications and management are transferable, but more dependent on the organization. My current role is much more IBM-specific than the others, and the complex parts of workshop leading are also IBM-specific, although the facilitation skills are transferable.
I need to learn more about the other paths to see what they look like and what the opportunities are. It’s good to explore different areas, because that will help me bring the different parts together later on.
I presented “Remote Presentations That Rock” as part of the IBM Best of Technical Leadership Exchange series.
What worked well? What can I improve next time?
I’ve written a little bit about our garden, but I haven’t reflected on how the garden helps me write and vice versa. Does it?
The usual benefits: Weeding and watering provide meditative breaks from the buzz of daily life. The garden teaches me patience and anticipation. It keeps me in better tune with the seasons.
What can I write that hasn’t been written before?
I tell people to never let the fear of clichés stop them from starting. After all, writing what has already been written is part of the process of discovering what is your own.
So: Why do I garden?
The mundane: I don’t want to pay the premium for cherry tomatoes at the supermarket, but I’ll happily invest in the seeds, soil, and screens to grow my own (even if it comes out to be more expensive).
The stubbornness of weeds inspires me, as does the revival of the sage that I’d given up for dead.
Composting fascinates me. It’s good exercise, too.
I love the everyday miracles of seeds growing into plants ever so slowly and quickly all at once. Every day is a game of spot-the-difference.
What do I like about gardening? Turning lawn and patio stones into lettuce and tomatoes, putting down roots, and reaching for the sky (perhaps with a little help).
An author wants to set up an interview with me because she’s working on a paper on what can be done through face-to-face networking that can’t be done online.
Here’s what I think:
Most people strongly feel that face-to-face networking is much better than online social networking. A paper that focuses on what can be done through face-to-face networking that can't be done with on-line social networking will find it hard to say anything that hasn't been discussed before. If you want to get attention and create value, you can teach people how to effectively blend on-line social networking with their offline social networking.
How can people use online social networking tools to make it easy to identify people they want to get to know, make the initial contact, find common ground, keep in touch, maintain their network, and make introductions?
People have heard a lot about how online social networks are limited and often a waste of time. What they need is guidance on how to use these tools effectively, and how to make it worth the investment of time. As more companies explore telecommuting as a way to cut expenses and reach more globally-distributed talent, people need to learn how to connect and stay connected at work and in life.
Hmm. Let me explore that, because I get a whole lot more done with online social networking than with offline ones, and I find virtual networking to give me better results – and surprisingly good serendipity – than offline networking events.
Why I like online networking investments (blogs, presentations, etc.) more than offline networking investments (networking events, lunch, coffee):
Where offline networking is still useful: hearing from people who don’t share online
What I would recommend to people who are starting out:
I’m starting to figure out what works for me in terms of gardening. I start seeds indoors on a seed tray, keeping them cat-proof by stashing them in an indoor greenhouse. I don’t get enough light indoors, so I move seedlings out to larger containers in a small outdoor greenhouse, or in the pots on the deck. As seedlings grow, I move them into the garden bed, where chickenwire keeps them safe from squirrels and birds.
How is this better than planting directly in the ground?
Things that are doing well: cilantro, lettuce (many kinds), tomatoes, spinach
@sachac Sacha! Your presentation provided a perfect, low-stress, socratic & fun contextual frame for my day-long workshop. We had a ball!
kjarrett on Twitter
@sachac LOVE your stuff! I use a couple of your slideshares for an online Web 2.0 class I facilitate. GR8 job! Keep em coming!
jdornberg on Twitter
This is why sharing is so cool. Even if I don’t have the time, ability, or network to explore the opportunities opened up by what I’ve learned, I can share those thoughts with other people, and they can go and do something awesome.
I put together the Teacher’s Guide to Web 2.0 at School because I needed to make a presentation to kick off the school year for 90 teachers. Since then, it’s been viewed over 20,000 times. More than 150 people have shared it on their blogs. I haven’t explored it further. I haven’t even posted any notes. In particular, slide #25 probably needs more explanation than the few keywords I put on there to help people remember after my talk. But it’s enough to tickle people’s imaginations, and the simplicity lets them fill in their own insights.
I like this. The more I share, the more awesome things I get to see, and the more inspired I am to share.
What can you share so that other people can build on it?
From the previous week’s plans:
Plans for next week
Russian birch plywood love © 2010 Sacha Chua – feel free to use it under the Creative Commons Attribution Licence
After Neko (our cat) joined our household, we discovered that we couldn't leave bagels and other bread products outside without the risk of nibbles from a cat with the midnight munchies. We'd been looking for a wooden bread box for some time, but the stores we frequent have only plastic and stainless steel bread boxes. So I found a simple bread box plan and borrowed the basic ideas from it.
I modified the plan to use butt joints instead of lap joints because we haven't figured out how to make proper joints yet. I also changed the plan to use 3/4" plywood all around, because we might wall-mount the bread box and the back needs to be sturdy. I changed the height, width, and depth of the bread box too, so that it was as long as the microwave but shallow and short enough not to get in the way.
After drafting the plans on graphing paper, I marked the pieces on a sheet of 3/4" Russian birch plywood, with 1/8" gaps for the kerf removed by sawing. W- cut the pieces using the circular saw and his straight-cut jig. I glued and nailed the box top, back, bottom, and front rail. The nails gave me a bit of trouble, but I'm getting better at driving them in.
With the center assembled, it was easy to trace the outline on the two pieces of wood for the sides, adding 3/4" near the top to account for the cover. I refined the lines with a ruler and W- cut them to size. While I attached the sides, he beveled the cover to fit.
All the bread box needs now are two hinges, a knob, and some kind of finish. It's a substantial bread box with plenty of space for bread and other things we want to keep away from cats.
If I were to do this again, I'd probably make it out of a thinner wood. Russian birch plywood is stronger than regular birch plywood, so 1/2" or thinner might do the trick. Russian birch is probably overkill for an everyday bread box, but (a) we had it, (b) I enjoyed working with it, and (c) the 13 layers of alternating dark and light wood look rather pretty on the front-facing edges. So maybe it would be regular plywood for the bottom and back (no exposed edges), and Russian birch for the sides, front, and top. =)
Oak bread boxes sell for ~$85 - 120 on Amazon.com. The 3/4" sheet of Russian birch was around twenty dollars. Sure, it’s not oak, and we spent a lot of time making things, but it was a great way to learn something new, create something useful, and enjoy a holiday with someone I love.
It occurred to me that many people don’t know what it’s like to enjoy working at a large company.
What is it like?
What I love the most about it is the ease and frequency with which I work with amazing people. No matter what the topic is, there’s bound to be someone who has thought long and hard about it and who’s passionate about it. If I’m lucky, there’s even a community of interest around it. It’s like the diversity of passions and interests you’d find on the Internet, except with a shared culture, clear identities, and ready access.
I like the fact that there’s always something happening. There are always new systems to explore, new tools to try, new ways to work.
I like the scale. I can share something that other people can build on, I can build on what other people have shared, and we can work on big things together.
I’m sure I’d love working in a smaller company or on my own as well. But hey, big-company life isn’t as bad as people sometimes think it is. Even in a company of more than four hundred thousand people, you can still find a way to rock. How?
Figure out what you do well and what you’re passionate about, and help people make the most of that. In a large company, there are plenty of opportunities, so find your way to something that fits you. For example, I enjoy helping people collaborate and I love building tools and systems, and my work has evolved to take advantage of those interests.
Get to know people in different parts of the organization. Take advantage of your organization’s size and scope by familiarizing yourself with what’s going on elsewhere, so that if you need to do something outside your area, you can get help from people who specialize in working on those things. I love using internal social networking tools to keep up with the exciting things going on in other departments.
Deal with the processes. A large company will have many standardized processes to keep things consistent. Don’t stress out about it. Adapt. If you have great ideas for making things better, figure out what and how you can influence, and be patient. Sometimes things are they way they are for good reasons beyond your experience, and sometimes things are ready for change. For example, our annual personal business commitments can seem like just another routine, but I use them to really think about what I want to do and how I want to measure success.
Take care of yourself. Take responsibility for your happiness and career growth. In a small company, it might be easier for people to relate personally, but you’re still responsible for your own growth. In a large company, it’s even more important that you realize that the best person who can look out for yourself is you. Me, I make sure I have the time and space to breathe so that I can keep bringing my passion and enthusiasm to work.
Empower yourself. Vision isn’t just the CEO’s job. Figure out your bigger picture. What’s your vision, and how can the company help you make things happen? The more your personal vision and the company vision are aligned, the easier it will be to work with the company to achieve big things.
During Laurie Miller’s presentation on digital eminence, she asked us to set an example for people by making sure all of our posts were relevant and meaningful.
That didn’t feel right, and I wanted to understand why.
I realized that I want people to see that they have permission to fail. To write boring things.
Blogs written by professional writers are inspiring. Tweets by stand-up comedians can be consistently amusing. But you can’t get to that point without slogging through the boring bits. Master photographers take thousands of pictures that will never see the light of day. The best baseball players still miss half of all their shots. You can’t be excellent if you’re terrified of imperfection.
You need to write badly in order to learn how to write well. That is, you need to give yourself permission to share, to make mistakes, to have errors and failed experiments.
So my contribution to helping people increase their digital eminence is this: I will be human, imperfect, and actively learning, and I hope that will help people see that it’s okay.
When I started writing, I discovered that it gave me ways to look at things I was learning and see if I could share them with other people.
When I tried drawing, I started seeing the structures and forms of things.
When I stumbled into giving my first presentations, I felt the dynamism of structure and conversation.
When I started exploring photography, I found myself looking at light and pattern and tone.
When I learned to sew, I couldn’t help but be entranced by the fabrics and seams of people’s clothes and accessories.
When I got into gardening, I became more aware of the seasons, sunshine, different kinds of plants, and different types of soil.
Now that I’m starting with woodworking, things around me are treasure-troves of lessons about woods, joints, and finishes. There is surprising beauty in a door when you think about how the panels float in a gap so that the wood can expand or contract. The smoothness of our shelves makes me smile.
I don’t expect much of my hobbies—just that they change my world.
How do your interests shape your experiences?