Ryan Nagle’s responsibilities include developing, deploying and supporting software for network members. His efforts focus on Project Largo and other open source tools to help members better present their journalism.
"Imagine you’re in a game with one objective: a bestselling product or service. The rules? No marketing budget, no PR stunts, and it must be sustainably successful. No short-term fads.
This is not a game of chance. It is a game of skill and strategy.
And it begins with a single question: given competing products of equal pricing, promotion, and perceived quality, why does one outsell the others?"
This guide is for those of us who are not regular Terminal/shell users and those who do not want to or can't use the Gitbub client to make changes to a Github repository.
While this is written specifically for folks that want to contribute to our docs repo, the steps are the same for any repo.
Keep in mind that our docs repo is just a collection of Markdown files, making it an ideal candidate for this approach to contributing.
Since you can't run your code on Github.com to test changes, the potential to introduce bugs by editing code directly on Github.com seems extraordinarily high, in my opinion. If you have a repo with actual code, I'd suggest learning command line git or dive into the Github client (for Windows, for Mac). The learning curve is steep, but the tools are more powerful.
Getting started
First of all, you'll need a Github.com account if you don't already have one. It only takes a minute. We'll wait for you.
From there, you can use the "Fork" button in the top right to create a full copy of the repository under your Github account.
If you are a member of multiple organizations on Github, you will be asked where you'd like to fork the repository. In this case, I want my fork of the docs repository to live under my personal Github account:
After you choose an account, Github creates your new fork and drops you on its homepage. Notice the URL and the message just below the title of the fork indicate that this is a derivative of another repository.
Step one: complete. Not so bad, right?
Editing a file
Now let's make a simple change to one of the files in our fork of the repository. In this case, we're making a superficial change to the README.md file as an example.
From the homepage of the fork, find the README.md link and click:
Once the README.md has loaded, look for the edit icon near the top on the right side of the page:
Once you click the edit icon, you are presented with a text editor where you can make changes to README.md:
Towards the bottom of the file, I notice that the spacing between the heading Version 0.1 and the list that follows is not consistent with the style of the rest of the document. So, I add a new line after Version 0.1.
Before and after:
Next, we must add a message to go along with our change. At the bottom of the page, you'll see an area labeled "Commit changes". In the first input, you should provide a brief but sufficiently descriptive message regarding your change, so that anyone browsing the history of changes made to the repository will have an idea of what changed and when. For example:
The secondary input is optional. I'm going to skip it here.
Once your message is in place, click "Commit changes".
Submitting a pull request
Great! So far you have your own fork of the repository and you're making changes left and right. But how do we let the maintainers of the original repository know that we have lots of good stuff for them to incorporate in the original?
The answer: submit a pull request.
To do so, go to the homepage of your fork. In my case, https://github.com/rnagle/docs. Towards the top, right side of the page find the "Pull request" link and click.
On the page that follows, you'll see something like:
If you've followed these instructions, the pull request page should default to settings that essentially say, "Submit a request to the maintainers of the original repository, asking them to pull in the changes made to my fork of this repository."
We'll gloss over the details of what each of the dropdowns at the top of the page can do for the pull request's settings. Let's stick with Github's sensible defaults for now.
Towards the bottom of the page, you can see the "diff" (i.e., difference) between the original version of the README.md file (left) and the changes made to the README.md file in your fork.
Now, click the big green "Create pull request" button at the top.
You'll be presented with another dialog, asking you to include any relevant details about your changes, consequences it might have for other areas of the repository, why your change is the "right way" to do it, etc.
Once you add a sufficient message, click "Create pull request" (this time, with feeling!).
Hooray! We now have an open pull request on the original repository's page:
Note that the URL and the repository title no longer reflect our Github account or fork of the repository.
What happens next?
When you submit a pull request, a notification is sent to the maintainer(s) of the repository. They can then review your proposed changes and either merge (e.g., pull) your changes back into the original repo or reject them.
In this case, since I am a maintainer for the original repository, the pull request page in the screenshot above displays a "Merge pull request" button. This will not appear if you do not have permission to make changes directly to the original repository.
Congratulations, you're now well on your way to contributing to our docs repo (or any other repo, really). Drop us a line in the comments or @INNnerds with your questions and pro tips for contributing to projects using Github.com.
Let's say you're having trouble with a WordPress theme or plugin that uses wp_mail. How can you inspect the email that wp_mail composes or verify that it is actually sending (or at least attempting to send)?
The DebuggingServer doesn't actually send email, so don't go checking your inbox. It's only meant to show you the email that would be sent, headers included, if it were an actual SMTP server.
Note that this guide assumes you're debugging wp_mail issues during local development.
Let's get started.
Set up the smtpd DebuggingServer
If you have Python installed (comes with Mac OS X and most distributions of Linux by default), this is the one-liner you can use to get the debugging mail server running. From the command line, run:
So that you don't have to remember that command, you can add an alias to your shell profile (e.g., ~/.profile), making it super easy to run the debugging mail server at a moment's notice.
To do this, open your shell profile in your favorite text editor and add the following line:
alias mailserve='python -m smtpd -n -c DebuggingServer localhost:1025'
Save your shell profile and source it in your shell to make sure the new mailserve alias is available:
$ source ~/.profile
Note: ~/.profile is probably the most common shell profile location. If you don't have this file, you can create one by running:
$ touch ~/.profile
Keep in mind that you might already have a shell profile for your specific shell. For example, ~/.basrhc for bash or ~/.zshrc for zsh. If you have a ~/.bashrc or ~/.zshrc, you can try adding the mailserve alias to one of them instead.
Once you have the mailserve alias defined and your profile sourced, running the server is as simple as:
$ mailserve
Note: there won't be any output from running this command initially. The debugging server runs, waiting for an application to connect and attempt to send a message.
Tell WordPress to send mail via the DebuggingServer
Now, in your WordPress theme or plugin, you can add some debugging code that will tell WordPress to send email via the debugging server you have running.
To accomplish this, add the following code to your theme's functions.php or to your plugin's main file:
This code changes the configuration of the $phpmailer object used by wp_mail, telling it to use the SMTP server on localhost, port number 1025. If you look back at the Python command used to fire up the debugging mail server, you'll see the $phpmailer settings correspond to the arguments passed in that command:
Once you have the debugging mail server running and the code above included in your theme/plugin, you can try sending mail with WordPress and see the entire message contents, SMTP headers, etc in your shell. Here's some example output:
vagrant@precise64:~$ mailserve
---------- MESSAGE FOLLOWS ----------
Date: Thu, 12 Mar 2015 16:21:54 +0000
Return-Path: <ryan@inn.org>
To: "\"Investigative News Network\"" <webmaster@investigativenewsnetwork.org>
From: Ryan <ryan@inn.org>
Cc: info@investigativenewsnetwork.org
Subject: [INN Website Contact] This is a test email subject line
Message-ID: <e538a998dbba308e2e6437a0b3ca4a50@vagrant.dev>
X-Priority: 3
X-Mailer: PHPMailer 5.2.7 (https://github.com/PHPMailer/PHPMailer/)
X-Originating-IP: 192.168.33.1
X-Mailer: WP Clean-Contact (vagrant.dev)
MIME-Version: 1.0
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
X-Peer: 127.0.0.1
This is the test email body.
------------ END MESSAGE ------------
Why do I need this?
This can be helpful if you're trying to track down missing parts of an email (e.g., hey, where'd my "from" address go?) or need to verify the contents or formatting of an email that your theme/plugin sends to users.
Keep in mind that, although this post describes how to use the Python smtpd DebuggingServer with WordPress, you can also use this guide with other applications as long as you can configure said applications to connect to the DebuggingServer.
We’re packing our bags and heading to Atlanta. See you at NICAR next week?
HOT LINKS
What we're reading this week
Adam: For new users of CSS preprocessors the allure of nesting is strong, but if you have to maintain the CSS you write for some time to come, it can really come back to bite you. I have felt this bite this week as we’ve been refactoring some of the navigation elements in Largo and it’s gotten pretty ugly, at times. Don’t let this happen to you.
Ben: Programming languages grow and evolve over time. How do the changes come to be? Boucoup has proposed an addition to JavaScript that would simplify raising numbers to powers. Their blog post on how they chose the syntax and what has to change to support it is, to my eye, fascinating.
Denise: I am in love with this new tool — and it’s acronym. LMGTDFY (Let Me Get That Data For You) is a free, open-source tool from U.S. Open Data that returns a machine-readable inventory of all the data files found on a website. Fun, huh?
Meredith: I share an interest in gaining a better understanding of code and how it impacts journalism (keeping up to my thoughtful teammates), I found a journalist based in the Czech Republic, David Bauer, who shares the story of his year spent learning code.
Ryan: A little computer science history for you this week: read about Grace Hopper — WWII veteran, early computer scientist and developer of the programming language that eventually became COBOL. Also, watch her 1986 interview with David Letterman.
Will: If you like computer science, and you like considering “what if,” check out this Reddit thread that asks r/compsci, “How different would Computer Science be if we were to reinvent it?” Maybe we’d live in a world where Java didn’t exist and PHP wasn’t so terrible.
In the beginning were computers, and women programmed them. A lot of women. So what happened? Turns out, computer programming isn't the first activity to shift from being relatively gender-neutral to being full of bros, broads and beer. Maybe it's time tech learned some lessons from skater culture (and I don't just mean how to look wicked cool in a hoodie).
Each week we ask someone from outside our team to contribute a link, tool or idea. Are you our next guest star? We think you might be. Send us a note at nerds@inn.org.
We Made A Thing
Our projects, manifest
This week, we shipped the first release of the PMP for WordPress plugin. Check out the code on Github and read more about the Public Media Platform.
Also, don't forget — the INN Nerds Book Club will meet during NICAR in Atlanta. We're reading and discussing The Design of Everyday Things by Don Norman. Join us!
Shout outs
Transformicons: A beautiful and useful library of animated icons, symbols, and buttons using SVG and CSS.
Adam: Even if you don’t work in public media, a new weekly podcast from Current.org called The Pub is worth checking out. A few episodes in and producer Adam Ragusea has covered topics ranging from how pick theme music to how women, people of color, and everyone else can sound more like themselves on the radio. Here’s the link to subscribe in iTunes.
Denise: Where do you even start looking for stories in all that Census Bureau data? Anthony DeBarros breaks down the household and relationship questions in this post from the Source blog and CensusReporter.org, with specific tips on where to find the most relevant data.
Meredith: "I read the news today, oh boy." The Guardian enlists Will Bulter of the Arcade Fire to write a song a day based on a Guardian article to be premiered on their site. Inspired in part by Bob Dylan, his week long residency begins Monday, February 23rd.
Ryan: I've been using submodules for quite a while now and never really had a problem with them. However, after reading Christophe Porteneuve’s post, I think I may have developed a mild case of Stockholm syndrome. This post covers the pros and cons of using git submodules to account for a project's external dependencies. It’s worth a read if you’re starting a new project and considering the best approach to this problem.
Will: User interface design is the responsibility of everyone involved in making a piece of software. That’s why goodui.org keeps a running list of ideas to make interfaces better. Not every idea is great (and might not apply to every product), but each has its merits.
As a multimedia editor at the Chicago Tribune, I often make decisions about publishing graphic content. This piece by Storyful outlines their method of vetting sensitive content as a news agency. But it also reminds us of our complex responsibility as journalists to balance being both analysts and messengers. The question we face is not just whether or not to publish, but also how to publish. As our industry evolves, we must always be having this conversation.
Each week we ask someone from outside our team to contribute a link, tool or idea. Are you our next guest star? We think you might be. Send us a note at nerds@inn.org.
We Made A Thing
Our projects, manifest
We’re excited to be working with The Public Media Platform (PMP) on a new WordPress plugin for their API to make it easier for public media organizations to share content with one another. We’ll be working on the plugin over the next couple of months and plan to release the final plugin late this spring. Look for future announcements as we roll it out over the coming months.
Shout outs
In anticipation of NICAR 2015, Ben Welsh and friends have updated their wonderful step-by-step guide to publishing a news app for the first time. Check it out: First News App.
Here's hoping your President's Day celebration is a blast!
HOT LINKS
What we're reading this week
Adam: Every time the design team at Medium writes about their work I’m at once super inspired, jealous and perhaps a little depressed. This week they published an epic series on how they think about typography for the site that has lots of tips that are applicable to any site and some that are...well, a little depressing because they would take forever to implement. But one can dream!
Ben: You’ve heard that journalists and their support organizations should be more conscious of information security, but you haven’t been sure where to start? Check out the book Learning Security: Information Security Education for Journalists by the Tow Center for Digital Journalism.
Kaeti: A lot of advice reads like a sort of infallible prescription for success, when in reality it’s just what happened to work for that person. Eileen Webb’s thoughtful essay reminds us that, “Our journeys are our own, and the key to each person’s success does not — cannot — lie on someone else’s path.”
Ryan: A few weeks ago, the developers at Facebook released React — a javascript library for building user interfaces. I’m always excited to try shiny, new libraries. React promises ultra-high performance, stateful components and an intuitive data pipeline. For a closer look at how to build things with React, read Thinking in React.
Will: If you're anything like me, you cringe at the thought of building rewrite rules. That's why you'll probably want to star this new repo by phanan on github — a list of .htaccess snippets to make at least that part of your day suck a little less.!
As a news developer, I strive to build graphics and applications that meet people where they're at. In most cases, that's been the web -- and more specifically -- the mobile web.
So, Quartz's recent story on how millions of Facebook users perceive the popular social network, not as a web site, but as platform in and of itself, shocked me.
Facebook's accessibility and reach around the world is important and gives journalists a great vehicle to share their content. But, we must remember that Facebook certainly has its own interests. As we ponder journalism's new model, let's hope we as reporters can convince these Facebook provincials that the web is just as diverse as the world they live in.
Each week we ask someone from outside our team to contribute a link, tool or idea. Are you our next guest star? We think you might be. Send us a note at nerds@inn.org.
Shout outs
The folks at Mapbox developed hubdb — a database adapter which uses Github for storage. Props to them for creative use of git and Github!
Some Other Stuff
Gather ye rosebuds
LISTEN: J Dilla passed away this week in 2006, but left us with his incredible music. To celebrate Dilla Day (Feb. 7, J Dilla’s birthday), give It’s Your World a listen.
COOK: Here in Chicago, we’ll be cooking up a hearty meal of shrimp and gritsto help get through this cold weather.
Adam:Design sprints are a technique Google Ventures has developed to allow the companies they work with to build and test nearly any idea in just 40 hours. Their findings will eventually be released as a book, but for now they’ve shared a list of articles and resources they’ve developed over three years spent refining their process.
Denise: I don’t know about you, but I usually do my best thinking in the shower. Probably because I can’t take my phone in there with me. I am loving WNYC’s Bored and Brilliant project, which is making the case to bring boredom back. The project has great stories on the science and consequences of smartphone addiction, apps to track your usage and a week’s worth of daily challenges to help you find a better phone-life balance. And if you’re interested in the journalistic takeaways from this project, Poynter has a good piece about WYNC’s innovative audience engagement.
Meredith: Courtesy of readme.io, this API Explorer Tutorial is an effort to banish boring static documentation. You can read and listen to more about interactive documentation.
Ryan: If you haven't had the chance to use Oculus Rift yet, I highly recommend seeking out the opportunity. As Zac Nielson covers in this piece, this device has the ability to create a level of "presence" (i.e., immersion) which is nearly impossible to achieve via traditional writing, audio and video. It's a tremendous innovation that has implications for a variety of fields. For news organizations, it means a new medium for story telling. What better way to create empathy than to convince your audience that they are truly a part of the narrative?
Will: I found myself rereading this piece on Vox Product earlier this week about how the algorithm to populate their homepage works. If you haven't read it yet, check it out!
The way a user navigates a website plays an essential role in how story unfolds on the web. This guide to efficiently simplifying navigation in Smashing Magazine includes a bunch of simple tips on how to keep your website’s navigation intuitive. As news organizations continue to experiment with novel ways of telling stories on the web, we’re seeing more outlets go beyond the basics to create interesting nonlinear narratives, such as NPR’s Borderland and the Tribune’s Shale Life series.
Each week we ask someone from outside our team to contribute a link, tool or idea. Are you our next guest star? We think you might be. Send us a note at nerds@inn.org.
In this post, we'll cover the basics of unit tests and walk through the process determining what types of tests you should write for your code. We'll also write a few tests to show just how simple most unit tests are.
The aim here is to make tests a bit more familiar and less daunting and mysterious. Unit tests are good. I'm by no means an expert on software testing. I'm just someone that's discovered the joy and freedom that writing tests provides.
If you are an expert on software testing and notice I'm wrong, please tell me!
Onward!
What are unit tests?
If you’re not familiar with unit tests, they’re essentially software written to test discrete units of functionality in a system.
Unit tests test the expected behavior of your software as it exists (or existed) at a specific point in time. They make sure that everything works as expected based on the rules established for the software at the time it was written.
If those rules change -- and your software along with them -- your tests will fail. When they fail, you'll gain insight into how the change affects the rest of the system.
For example, if a change is made in code for your site’s user profile management, you'll want to make sure that the change does not affect the behavior of your site’s payment processing code.
Tests can save time and money by acting as the first line of defense in catching problems before they result in a late night emergency or catastrophic failure. When things go wrong, they can also serve as the first response team. You can run your unit tests to see if any rules/expectations have been broken by some recent change, either in your code or some external API dependency, and fix the bug introduced by the change.
Sometimes the tests become outdated. Again, the tests will fail, letting you know that you’re no longer testing for the right thing. When this happens, you should update your tests to make sure they are in line with the new rules for your software.
Getting started
Unit tests are rarely difficult to write or run if you’re using a testing framework (e.g., phpunit).
You can write your tests before you write your business code if Test-driven Development (TDD) is your thing or you can write them after.
It doesn’t matter much when you write them as long as you have them.
The brilliant thing is that almost all tests you write will be stupid simple.
Tests usually assert some condition is true (or false, depending on what outcome you expect).
On top of that, most testing frameworks have convenience functions built in for performing these types of tests and several others, including testing the expected output of a function (i.e., what the function prints to the screen).
The three test functions we've used most frequently when writing tests for Largo are assertEquals, assertTrue and expectOutputString.
Our stupid simple example plugin
So, let’s say that we want to write tests for a WordPress plugin. Our plugin simply prints the message “Hello world!” in the top corner of the WordPress dashboard (sound familiar?).
<?php
/**
* @package Hello_World
* @version 0.1
*
* Plugin Name: Hello World!
* Plugin URI: https://gist.github.com/rnagle/d561bd58504a644e9657
* Description: Just a simple WordPress plugin that prints "Hello world!" in the top corner of the WordPress dashboard.
* Author: Ryan Nagle
* Version: 0.1
**/
function hello_world() {
return "Hello world!";
}
function hello_world_markup() {
echo "<p id='hello-world'>" . hello_world() . "</p>";
}
add_action('admin_notices', 'hello_world_markup');
function hello_world_css() {
$x = is_rtl() ? 'left' : 'right';
echo "
<style type='text/css'>
#hello-world {
float: $x;
padding-$x: 15px;
padding-top: 5px;
margin: 0;
font-size: 11px;
}
</style>
";
}
add_action('admin_head', 'hello_world_css');
Couple of things to note:
We have one function -- hello_world -- that returns a string.
Two other functions -- hello_world_markup and hello_world_css -- that echo strings but have no return statements. This means we'll have to check the output of these functions rather than the return value to properly test them.
Note that hello_world_markup relies on hello_world to provide the the “Hello world!” message.
So, how do we test this plugin? Well, if you don’t have your test framework installed and configured, you’ll want to get that ready and raring. The details are beyond the scope of what we'll cover here, but for an overview of setting up unit tests for WordPress plugins and themes, see Will Haynes’ Unit Testing Themes and Plugins in WordPress.
The INN Nerds also have a collection of deploy tools to make developing WordPress sites and writing tests much easier. You can read about using the deploy tools for testing themes and plugins here: Updates to INN’s Deploy Tools.
Disclaimer: getting unit tests working WordPress can be daunting at first. It’s a lot to digest and may take some time to set up if you’re coming to this cold, so be patient. It’ll be worth it.
If you’re really dedicated to the cause and need a hand, reach out to us.
How do I write a unit test?
With that out of the way, let’s design some unit tests for our “Hello World!” plugin.
The first thing we’ll want to do is enumerate the features of the code which we need to test.
There are many ways you can approach this. My preference is to create a single test file per file in my source code and have my tests directory mirror the structure of my source directory.
Each test file has tests for each function (or member functions of classes) in the corresponding source file.
So, the directory structure for our plugin would look like:
We’ll be doing our work in test-hello-world.php where we’ll set up the skeleton of our test case, which is as simple as extending WP_UnitTestCase:
<?php
class HelloWorldTest extends WP_UnitTestCase {}
We can then stub out test functions for each function:
class HelloWorldTest extends WP_UnitTestCase {
function test_hello_world() {
// Test hello_world()
}
function test_hello_world_markup() {
// Test hello_world_markup()
}
function test_hello_world_css() {
// Test hello_world_css()
}
}
Now, let's look at each function and consider what we're testing:
1. For hello_world, we want to verify that the string returned is "Hello World!":
function test_hello_world() {
$this->assertEquals("Hello World!" == hello_world());
}
Easy enough. Now if for some reason someone changes the return value to "Hello world!" -- capitalization be damned -- the test will fail.
I can hear you now, "This is stupid, no one writes code like this." Yes, the example is stupid and that's the point. Don't get caught up focusing on the wrong thing.
It makes no difference how complex the function is, the only concern is verifying that the return value or the outcome is what is expected.
So, if instead you're testing some_made_up_function which returns an object, you may want to verify that it is actually a PHP Object:
2. For hello_world_markup, we want to verify that the function prints "<p id='hello-world'>Hello World!</p>":
function test_hello_world_markup() {
$this->expectOutputString("<p id='hello-world'>Hello World!</p>");
hello_world_markup();
}
Notice that we're expecting "Hello World!" to be part of the output. This might not be a good thing. If the return value of hello_world changes, this test will fail, too.
For the sake of example, let's say we only care about testing the markup and not the message. We can take this test a step further and decouple the two so that we're only testing the markup by changing it to read:
Essentially what we're saying is, we don't care what the message is as long as it is wrapped in <p id="hello-world" /> tag.
Simple, right?
3. For hello_world_css, we want to verify that the function prints the CSS rules for our "Hello World!" markup:
function test_hello_world_css() {
ob_start();
hello_world_css();
$result = ob_get_contents();
ob_end_clean();
// Make sure there are style tags being printed. Duh.
$this->assertTrue(!empty(preg_match("/.*?
One of the goals for us was to create a resource that any of our member organizations could use to bolster their coverage of the elections — either by hosting information for an entire state, or including individual power player cards in articles covering campaign finance and the election.
Thus the embeddable versions of the state and power player pages. These are essentially the same as the normal templates, with a simple full-width layout and simplified INN branding (a credit link at the bottom of each embed).
The Kentucky Center for Investigative Reporting is one INN member making great use of the app. Here is a complete list of all the members organizations participating in the project and the stories they've published (so far).
The entire project is responsive (thanks to pym.js, yet another NPR project), made to look great no matter what size container the embeddable elements get placed in.
Snags and snafus
In working with the NPR app template we encountered some things that aren’t well documented (yet).
CSS and JS pseudo-tags
Tyler covers this briefly in another blog post on the app template.
What wasn’t clear at first was how the JS and CSS pseudo-tags interact across templates.
We ran into a problem where, in separate templates, we “pushed” or queued different sets of javascript files to be rendered. In both templates, we were passing the same file name to the render function, which resulted in the second file overwriting the first when deploying.
Once you realize that JS.render outputs a file, the contents of which are determined by preceding calls to JS.push, you realize that having different calls to JS.push before rendering to the same file just won’t work.
In this case, if template 2 is rendered after template 1, “js/app-footer.min.js” will be missing “underscore.js”, potentially breaking functionality in template 1.
By making the filename passed to JS.render unique to each template, we can be sure we’re not clobbering any javascript files.
Flask’s url_for function and your project’s path prefix
Another issue we encountered was that the app template, using Flask’s default url_for function, doesn’t take into consideration your project’s path. That is, when you deploy your app to S3, it is meant to live at something like http://apps.yourdomainname.org/project-slug/ whereas the development server uses something like http://localhost:8000/ without the project slug.
For example:
<a href="{{ url_for('some_view') }}">Hey, a link to a page</a>
Renders as:
<a href="/some-view/">Hey, a link to a page</a>
What we want when deploying to staging or production is an URL that includes the project slug:
<a href="/project-slug/some-view/">Hey, a link to a page</a>
To remedy this, we created an app_template_url_for function to replace Flask’s standard url_for. The app_template_url_for figures out the current target environment (i.e. development, staging or production) and inserts the project slug as necessary.
This provides the flexibility to include assets outside of the CSS and JS pseudo-tag framework if you find yourself with the need or desire to do so.
Working with Member Organizations
The Power Players project presented other challenges, too, because of the number and diversity of INN member newsrooms participating.
First, the data needed to be analyzed and populated in the app template for about a dozen states. Campaign finance data is notoriously dirty (especially when trying to look at individual donors, as we were), plus we were combining data from different sources, so the cleaning and analyzing stage took longer than expected. Some member organizations also wanted more custom analysis, such as looking at local data only rather than statewide data as most members were. This forced us to make some compromises in the design of the app like using the state outline for California but labelling it “San Diego.”
A project such as this also takes a great deal of communication. INN’s director of data services, Denise Malan, held a kick-off conference call with members and stayed in contact with them as she was analyzing the data and the tech team was designing and building the app. We also provided members with instructions on how to add photos and customized content to the app while it was still a work in progress.
As is the case with all our editorial collaborations, INN members can publish stories according to their own timetables, and several ran their stories before the app was finished because we were working right up to the deadline on Election Day. Others have yet to publish because they choose to wait for updated data with post-election numbers.
Ideally, we would love for all members participating in a project to use our work, and we learned some valuable lessons in the process of building our first editorial app.
In future projects, we would like to have mockups of the news app ready as early as possible so our members are able to visualize what we will be providing and how this will fit with their reporting. We also want to provide a firmer deadline for launching an app so members can plan their publishing dates accordingly and leave time to implement the stuff we build. We’ll also do a better job providing the partners with documentation to make it as easy as possible for them to adopt our work.
Conclusion
We learned a lot in the process of building our first app, both about the NPR app template and also what it takes to manage a complex project working with so many partner organizations.
Will we use our fork of the NPR app template for everything? Probably not. We’ll continue to experiment and try out different things before settling on our default set of tools and templates. For projects where it’s a good fit or where we need to deploy something quick and easy, we definitely plan to use it as a solid starting point in building future apps.
Since this is our first app and we’re still learning, we’d love to hear your thoughts and feedback. You can find our team on Twitter @INNnerds or send us email at nerds@inn.org.