A business case

TLDR: The long and random argument I make to myself about why I’m going to start putting energy and resources into promoting something that could be stamped finished.


Time to make a decision

I’ve been thinking about my EFL worksheet generator. The thing is this: it does what I want, now. Almost exactly how I want it to.

I could declare it finished. That’s a crazy idea to me.

And yet, is it amazing if I’m the only one who knows it’s amazing? What’s more, ever since I decided to move the thing to the internet, as opposed to making the desktop interface better, I’m paying hosting costs. Not a ton of money, but much more than I would pay for a website like ESL Library.

Should I move it back offline?

I’m asking myself these questions because I’ve realized that this will never be a completely finished project. I’m taking a Udemy full-stack bootcamp, because there are ways the interface could be more amazing.

But, as I started looking into how to promote a web page, and even paying money to experiment with AdWords, I realized I wasn’t sure that was what I wanted to do. Am I the kind of person who gets excited about making and then promoting something? It doesn’t feel like who I am: I’ve never really felt comfortable saying “look at me!”

At the same time, it’s clear that “if you build it, they will come” is not a great strategy to follow.

So, I have to pick a future for the project.

The case against monetization

There are three big arguments against pursuing monetization. First, is the fact that I’m not really opposed to monetizing the website, it’s the fact that I’m opposed to investing a lot of energy promoting it. It’s just that I don’t think I can have one without the other. (Well, I could try and promote it without monetizing it, but then I’d be increasing my workload for… I’m not sure what for. Ego?)

The second argument against the whole thing is that I’m not sure it fits into my sense of who I am. I like the idea of being a “maker.” It’s a cool title and one I feel like I can give myself. There’s something existentially satisfying about having an idea and turning it into a reality.

Marketing, on the other hand, doesn’t feel like who I am. Or, I’ve never looked up to people who are great at promotion. The title “promoter,” to my ears, doesn’t sound quite the same as the title “maker.”

The last argument is time. I want to learn Latin. I want to work on other projects. Some of them are already pretty fleshed out as ideas. And, the worksheet generator is threatening to turn into a project that will take over my existence. Some of the things I considered doing as part of promoting it include:

  • Continuing to prioritize making free EFL materials.
  • Starting a blog focused on EFL teachers in Germany, to promote the site.
  • Making online tools to automatically generate materials from a text. (Copy-and-paste text from a company’s website to get a gapfill exercise prepared.)
  • Making a YouTube channel

The thing is, any of those could become a time-suck. Trying to do a combination of those and perhaps also paying for promotion… Well, it would require me to make a lot more money off the site to make it worthwhile.

The argument for monetization

On the other hand, as things stand now, the site doesn’t ‘feel’ finished, because nobody uses it. If someone told me he was proud of the site he’d made, but he was the only one who used it, I don’t know if I’d be as impressed with that person as I am with myself right now.

What’s more, a lot of the things I want to make are web-based. If they all incur costs, is there a point if I don’t know how to attract users? Why work hard to make the next site, if it’s just going to be a tool that I use on my own?

And, further, I could use the money. Not in the Elon Musk the-rest-of-my-career-is-a-working-retirement kind of way, but in the more modest what-will-I-do-if-I’m-ever-too-sick-to-work-for-more-than-a-week kind of way. A passive income would go a long way towards stability.

That brings me to the last argument for monetization (and the stress of promotion): I don’t want to be a career coder. I love making stuff, but I don’t think I could bear the frustration of coding just for the joy of coding well. The fun, to me, is in turning a dream of my own into a reality. I’m getting better at all this (though, by no means good), but I wouldn’t want to wind up in a spot where I have to go look for a job as a novice coder in my 40s, in Germany.

That means that, if I’m going to keep doing this, I’m going to be responsible for building entire projects, from start to finish. And, well, by that standard, the website isn’t finished. If I want to make an app to use in the classroom, or the fantasy pilgrimage website, all of these things will only work if I can both make, and sell them.

The business case

Here’s the last bit of reasoning: What would it mean for the website to be worth my while? By which I mean money. How much money means “this is why I do all this extra work?”

Obviously, there isn’t a maximum, but €500/month would mean I could quit one of my jobs and reduce the number of hours I have to work. That’s 50 people willing to pay €10/month for the service. Or, 71 people willing to pay a discounted price (if there’s a €10/month, €90/year kind of option).

Needless to say, I think those are great prices for what the site does (and for the amount of work I do making it work).

I haven’t been able to find out how many people teach English to adults in Germany, but I’m confident that there are at least 50 in Dresden. So, if I focus on the German market… the business case is compelling.

It’s compelling if I can learn how to communicate about what I do, and why it’s great.

Advertisements

I fixed my memory!

Yesterday evening I said to myself “I’m going to tackle adding crosswords as an available review activity for dynamic-efl.com.”

It was an activity that I’d been postponing since I got the whole thing web-based. To be honest, I don’t know why anymore. I just remember the feeling that it seemed like a lot of work and, hey, wordsearches were working, so why rush, right? But, whenever I listed everything that the website could do, I’d find myself tagging on “and I hope to add crosswords, soon, too.”

Which was ridiculous because they worked in the desktop version, so whatever I needed to get done to make them work on the web version was probably just a matter of a careful reading of the code.

So, yesterday I resolved to get them done, and I did. Easy as that.

I was almost surprised how easily it all worked. And, to be honest, it’s a bigger accomplishment than updating the memory activities, but who could pass up the opportunity to write a blog post titled “I updated my memory.”

 

crossword
Funnily, this one still includes multi-word vocabulary. I decided to exclude them from crossword activities in order to make things easier on the learners.

 

Inspired by that, I decided to tackle something else that’s been outstanding on my list: fixing the memory activities. The memory activities worked fine, of course, but there were two different activities: translation memory and, if a group had enough vocabulary with associated pictures, picture memory. It seemed most logical to make a single memory activity that would use pictures where they were available, and translations where they were not.

What’s more, the translation-only memory tacked a ‘cheat sheet’ on at the end for the teacher, in case I forget how I translated a certain word (it happens to me, and I wrote the translations. I guess it happens to others, too). Why not do the same thing, but with pictures and translations. In case one of the pictures isn’t clear.

So, this evening, I fired up the IDE and I got it done. There were a few hiccoughs along the way, but it was done in less time than it took for the kids to watch a movie I didn’t like. So, hooray for that, right?

It’s nice to feel productive. In fact, so nice that I’m not even excited about now getting to work on preparing for classes next week.

Adding Mailchimp actions to a Django model

As I get ready to try a second time to attract users, I decided to use MailChimp mailing lists to keep in touch with these users, as well as setting up some ‘tutorial’ emails at regular intervals. (I’m imagining something like “You’ve been using dynamic-efl.com for a week now, did you know you can do this to get more out of it?”)

To do this, I’d have to have code in Django that automatically signed members up. That means that this is my first attempt to work with an API, even by way of a library.

I decided to go with the mailchimp3 library and installed it in my virtualenv according to the instructions and set up some variables in my settings.py file.

# Mailchimp stuff
# Authentication
MAILCHIMP_LOGIN = 'login'
MAILCHIMP_KEY = '===>SECRET<==='

# The lists
MAILCHIMP_LISTS = {'welcome': 'listID',
                   'germany': 'listID',
                   'beta-testers': 'listID'}<span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>

The idea here is that I’ll be able to iterate over the keys in the lists, if I ever need to check that the user is in all the lists (for example, removing a user should include removing him from all lists).

To that end, I added a function at the beginning of my models.py file. (This probably isn’t good form).

def mailchimp_get():
    """ Returns a logged-in mailchimp client """
    return MailChimp(settings.MAILCHIMP_LOGIN, settings.MAILCHIMP_KEY)<span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>

It seemed like the easiest way to only write the login once.

Finally, I had to create a few methods in the Model in question (in my case, this is my teacher model, as my users are called ‘Teachers’ in this bit.

def mailchimp_add_teacher_to_list(self, listname):
    """ Takes a listname, checks that the teacher is not a member of the list, then adds it. """
    client = mailchimp_get()
    listid = settings.MAILCHIMP_LISTS[listname]
    if not self.mailchimp_teacher_in_list(listname):
        data = {
            'email_address': self.user.email,
            'status': 'subscribed',
            'merge_fields': {
                'FNAME': self.user.first_name
            }
        }
        client.lists.members.create(listid, data)

def mailchimp_lists_get(self):
    """ TEMPORARY - Returns a list of all mailchimp lists """
    client = mailchimp_get()
    return client.lists.all(get_all=True, fields="lists.name,lists.id")

def mailchimp_teacher_in_list(self, listname):
    """ Returns a bool indicating if this teacher's email address is in the list """
    email = self.user.email
    client = mailchimp_get()
    results = client.lists.members.all(settings.MAILCHIMP_LISTS[listname], get_all=True, fields="members.email_address")
    members = results['members']
    addresses = []
    for m in members:
        addresses.append(m['email_address'])

    return email in addresses

I think the names should be pretty self-explanatory. The mailchimp_lists_get() method was just an experiment based on following the instructions in the documents.

Lastly, I created a view and a URL just to test these functions by printing to the console and looking at the (currently empty) lists in mailchimp.

Summary

It was my first time sitting down and thinking critically about what I wanted and how to do it and I have to say that mailchimp and the library basically made it painless. I have other project ideas that will require more API integration, and it’s made me hopeful.

It’s easier to expand than to fix…

I feel like everything I learn about coding is already a well-known software truism. But, I wanted to report in on my progress with the EFL worksheet software by saying that it’s moved back to the top of the priorities list and I’m working on it, but slowly.

There was a bug where the gap fill sentences created for capitalized vocabulary (‘Baltic Sea’ and ‘Vienna’ are examples) didn’t work correctly.

I immediately realized that the code put the entire sentence into lowercase (with Python’s .lower() method) before looking for the word. No problem, I thought, I’ll just find where the word is searched for and have it search for the lowercase text of the word. Problem solved.

I genuinely thought that was something I could do quickly, by way of getting back into the coding game…

More than an hour later, I found the spot where I forgot the extra .lower() call and everything was fixed. But I needed a full hour to figure out where in my code that happened.

A full hour.

I’m discouraged that I had such a hard time finding my way through my own code.

Updating the Interface

I have been busy, and frustrated, as I work on the worksheet generation app. And, unfortunately, I haven’t been adding new functionality or improving the existing functionality. At least, I haven’t been improving it from the point of view of how the worksheets are made. I’ve been working on how people make the worksheets.

This is what the app interface used to look like:

old-formattingIt really isn’t bad, it’s just very basic. I knew I’d change it at some point — I use this site more than anyone else, and I have an idea how I want it to look — but I wanted to focus on features.

Then, recently, I invited friends and colleagues to try the website out. And nobody seemed to understand what it did. I told them (in emails, in person) and they loved the idea, but the interface that seems obvious to me… well, it obviously isn’t.

I had an idea: I’ll make a sort of tour you take, before you can sign up, that will explain the functionality with screen-grabs. 

But, there are so many things on my plate, I know the chances of me making the same tour twice are not great. At least, not in the same year. So, logically, the interface should be changed.

CSS is hard!

The thing about CSS is this: you can  download templates (like I did for the landing page, from a great resource called Start Bootstrap), and I figured I could easily adapt the rest of my page to match it.

Wrong!

To be fair, I haven’t yet come across a CSS problem that, once I figured out how to articulate it correctly, didn’t have a clear answer somewhere. (Also, I love the resources provided by W3Schools, but hate their search functionality. Are they trying to make money there?) But, every step felt like an uphill battle.

Eventually, I got to a point where I was starting to be satisfied with the result.

Hosting static files on django is poorly explained!

I’m starting a list of things that were hard for me to learn about django, to eventually explain when I feel like I understand them. One thing would be the deployment to linode (remember that?) and another would be hosting static files.

After a lot of googling and basically blindly trying things, I’ve stumbled into a configuration that worked. But, for a while, it only worked on the linode site and not locally and I was too frustrated to change it. So, every change I made had to be pushed to the site to be tested, which was also frustrating.

Then, I got around to working out my development file hosting and was able to test changes more quickly (and without feeling like I’d have ‘broken’ the site just then when a friend would try to use it), but when I pushed my changes to the linode server, I realized that I’d broken hosting there!

Now, things seem to be working both locally and on the server, and I’m going to not play around with it. But, I have other projects cooking, and I’ll need to wrap my head around this.

So, what does it look like now?

Obviously, after all that drama, you’d expect something amazing, right? Well, it turns out that I can fix individual problems, but I don’t really have much of a vision of an overall design. So, while the layout is basically the same, the new formatting is something I’d rather spend my time looking at:

new-formatting

It has a title bar that remains fixed as you scroll, and functions are presented as buttons (because they seem to suggest action) and, rather than telling you “there is enough vocabulary to make a memory activity,” it simply doesn’t show you the option unless there is sufficient memory.

I can live with this for the rest of the academic year, I think. Now, I just have to clean up all the pages and make a tour, as well as finishing up my ‘minimum functionality…’

Preparing for Clowns, or what I learned from a Twitter-fight

The CodeNewbie chat last night was about dealing with bullies, and, since that dovetailed so well with what I’m working on right now, I thought I’d try to be a bit more active in blogging about what I’m doing.

You see, I think my app is 90% finished, but I’m taking a break from features to get ready for the public. Which, really, means, to get ready for the jerks. You see, the worksheet generation app isn’t a social app. The user’s interaction with it is basically all centered on the user and their own groups and classroom resources for their groups.

But, contributions are shared. That is to say, if you need a translation for the word ‘web app’ for one of your groups and there isn’t one in the system, the system just asks you for one. But then, the next guy profits by the translation you added. And you’re profiting from the resources added by others.

The Twitter Fight

But then, I got in a bit of a fight on Twitter. I’ll post sometime on the idea of ‘defending the ancestors,’ and why I don’t like the term ‘the ancestors’ being used as a dog-whistle for racism (racist dogs!) but that was what it was about. And it wasn’t nice.

While I’m a middle-child and can handle Internet strangers being mean to me, I realized that the guy I was fighting with had the advantage: his Twitter handle was only for his trolling. My Twitter handle connected to all my life, and he could see that, use it in a fight. And, because I tend to fear the worst, he could start being a jerk in other parts of my life.

I realized that, when I start posting about the worksheet generation app by name, with links, anybody who felt like they didn’t like me (and there have been a lot of those people in my life) could create a free account, and just throw a wrench in the works by adding terrible resources.

Imagine if a legitimate user — maybe even one paying for the service — wanted that aforementioned translation of ‘web app’ and got something like ‘where we see the naked photos of your mom.’ Even worse, what if that person — like I often do — didn’t proofread that worksheet before handing it out to students. (At some point, worksheets will be emailed with a mouse-click.) What a nightmare.

Worse than paranoia

I talked myself back down from “Twitter people will be mean.” After all, the solution could easily be ‘just charge everyone.’ Who’d pay me money for the privilege of trolling me?

Then I realized I shouldn’t worry about Twitter trolls making accounts and being jerks. I should worry about people just like me thinking that they’re being funny. Or, even worse, thinking that the resource they’re making is appropriate for their students. After all, we teach adults, whose to say that we can’t include “your mom” jokes?

My real  nightmare is someone either testing out the system, playing around, and putting random crap in… Just to see if it really appears in a worksheet.

Of course there’s a solution

I can deal with it. Of course I can. There’s not a problem without a solution.

My interim solution is, for now, giving users the opportunity to explicitly say “this is part of an inside joke with this group.” You can easily add resources that will only be used for a single group. Easy.

The other thing is pretty simple: all resources are immediately available to the person who creates it. Everything else has to be approved, first. As users are added, it will be possible to say “the resources added by user_x will be automatically available,” but, until then, it means that I’ve invested a lot of time already (all morning today was spent creating the interface and the backend that will enable this level of moderation) into the project of investing a lot of extra time in this project.

And that kinda stinks. It makes me like people, as a group, just a little bit less.

Working with Working Code

This is a tales of pros and cons. I set myself the goal of creating my worksheets this (academic) year using my worksheet generator. The results have been mostly positive — the students don’t know it, but I’m proud as hell of the worksheets I hand out, the students are benefitting from the way vocabulary is repeated, and even the errors are room for good, spontaneous discussion — but mixed.

Basically, I’m leaving PyCharm open all the time, because if I’m at my computer, it’s either to improve the code (i.e., do some coding) or to use the code for its intended purpose. Somehow, I feel like I’m a one man example of the idea of producing “banana software,” developing features as I need them, often under a deadline which, though it may be self-imposed, feels ominous. (Right now, I’m taking a break from coding a new type of exercise which is called for in next week’s lesson plans — sure, I could just cancel it [I have a great relationship with the customer], but that would mean admitting defeat.)

The pros:

Basically, what’s great about what I’m doing now is that I keep realizing that my planning was incomplete. It’s in the usage of the software that I realize  how much more it could do. Some changes are just inserted as TODO comments in the code like “It would be great if this worked with that” others I implement immediately, because I realize it makes no sense to code the rest of the features if these basic things are going to change.

Even more, it keeps me at coding. Knowing that this stuff has to be finished by next week keeps me at my keyboard when maybe I’d like to be in a hot bath. And that keeps everything fresh in my mind. Great.

The cons:

Obviously, the pressure is a double-edged sword, but I generally embrace it. However, it does mean that I feel this pressure to code forwards, rather than backwards.

I don’t know if coding has directions. What I basically mean is that I feel the need to add the features, rather than improve how past features work. An example is from the exercise I’m coding. It’s being coded as an object and I’m copying and pasting so much from other exercises that, even though there is a base class that they all inherit from, there should be a second base class for this category of objects.

And I could get that done in less than an hour, probably. But I don’t have that hour.

The thing is this: I know me. I’m proud of accomplishments that I can see. I don’t know that I’ll ever come back to this once everything is working, because cleaning up code doesn’t feel like an accomplishment the way that writing code does.

Maybe, I need to work on myself as much as on the code.