Last month we drafted Franklin, our DNA sequence annotation tool written with React. This month, we decided to learn Redux and we designed advanced features to make Franklin a useful tool for biologists around us.

Le lab #4 - Franklin the DNA annotation ninja is back

After our last Le lab session dedicated to Franklin, we had great feedback from people around us and beyond. Yet we only produced a POC, not a tool suitable for a realistic analysis. With this week changes, Franklin is now a reliable tool to analyze and annotate your DNA sequences. In the sequel, we describe those new features and how we built them.


Hurry to test new features?

Try Franklin now!


From a custom Store to Redux

Until recently, our React-based applications always relied on the same principle: React for the View layer, a single Store for the Model layer, both glued with a single Controller. The main advantage of doing this lied in its simplicity. Yet, this was not future-proof.

While our previous architecture complied with Flux, we wanted to learn a new framework during this session (that is the aim of such sessions after all). We naturally chose Redux, which integrates well with React.

Rewriting our Model layer into Redux (PR #46) was not too complicated because Redux makes life rather easy. We especially felt in love with the principle of reducers that are pure functions. Testing such functions could not be easier, and we found ourselves writing even more tests! We split our logic into different reducers to keep things readable and simple. Talking about simplicity, we adopted the Ducks approach for bundling reducers, action types and actions into “modules”:

$ tree app/modules
app/modules
├── __tests__
│   ├── exon-test.js
│   ├── franklin-test.js
│   ├── label-test.js
│   ├── search-test.js
│   ├── selection-test.js
│   └── sequence-test.js
├── exon.js
├── franklin.js
├── label.js
├── reducer.js
├── search.js
├── selection.js

Our franklin module is a bit special: it does not have any reducer, only action creators that are thunks (returning a function instead of an action). The reducer file combines all reducers with combineReducers() to build a single reducing function for the store.

The next step was to slightly modify our View layer, by introducing “connected components” a.k.a. container components. We chose to design most of our components using different files as shown below. Given a Search component, the index.js file is a Higher Order Component, which is aware of Redux and defines mapStateToProps() and/or mapDispatchToProps(). The presenter.jsx file contains a presentational component, which is often a stateless function. Finally, the __tests__ directory contains the test files. Thanks Robin Wieruch for the inspiration by the way!

$ tree app/components/Search
app/components/Search
├── __tests__
│   └── Search-test.js
├── index.js
└── presenter.jsx

We are happy with this new structure, and we applied it to the whole project (PR #61). During this session, we identified similar components that could be merged and reused. We started to refactor our code (e.g., PR #67 and PR #69), but there is still room for improvements. Additionally, we now feel the need to split a lot more components into smaller ones.

Le lab #4 - Franklin Pulse

Introducing Redux forced us to rework a few components, but the code base has been simplified, and we spent time writing many more tests. This resulted in a huge amount of new features shipped during this session.

Ninja features

The first release of Franklin focused on rendering SVGs displaying a nucleotide sequence that can be annotated with custom labels. After having moved to Redux, we put some efforts on adding useful features to make Franklin almost usable for realistic analyses.

Built-in exon support

When a biologist analyzes a genomic sequence to design her experiments, she needs to know where the exons are located. To make a long story short, the first step of a gene expression is the production of a molecule called a messenger RNA (or mRNA). This mRNA can be modeled as an alternation of introns and exons with flanking untranslated regions (5’ and 3’ UTR resp.). During the mRNA maturation process, introns are removed while exons are kept to be translated into amino-acids to produce a protein.

Now that you see the correlation between these particular regions of a gene (exons) and this gene expression product, you should better understand why we needed built-in support for exons in Franklin. As you can notice below, exons can now be distinguished from standard annotations on the sequence under analysis:

Exon representation in Franklin

Furthermore, now that we are able to distinguish exons from custom annotations, it opens the way to new features like:

  • cDNA representation: only exons (and UTRs) are displayed;
  • sequence pattern search in this cDNA.

Those features are not implemented yet, but we will describe them more finely in new issues and, hopefully, work on it in the future. If you want to do open source for scientists, we can work together by the way.

This feature is the one that usually produces the Wow effect! Searching for a pattern in your sequence of interest in real time is pretty useful. Let’s see it in action:

Franklin's search feature

If you pay attention to the Gif above, we have implemented multiple selections support, allowing you to quickly add many annotations based on your search result with two clicks: select the label, save and :tada:!

To implement this search feature, we used Keith Horwood’s NtSeq library that is part of the BioJS registry. We have to admit that the highly optimized exhaustive sequence mapping tool he developed makes Frankin search feature blazing fast! Hat tip to him.

Last but not least: sequence search supports IUPAC notation, yay!

Custom starting position support

When loading a new sequence, Franklin considers that your sequence starts at position 1. This can be really frustrating for biologists that are used to manipulate genomic positions as stated in the literature. So we fixed this by adding custom starting position support:

Franklin's custom starting position

When the starting position is changed, every position updates instantly. But a problem still remains: for now, annotation and exon positions are NOT updated. These are not updated, because we do not know if the user entered literature positions or positions selected from the sequence.

This is clearly something that needs to be improved in the near future, but it is a tough problem. We would love to hear from you on this point.

One more thing

We pushed numerous other improvements and bug fixes, but we still did not implement any persistence layer yet. This means, refreshing the page resets everything. A first step would be to implement redux-persist, but it was out of this session scope.

Final thoughts

For this new session, we are pretty satisfied since we have carefully respected our Le lab schedule. Compared to the previous one, working three consecutive days helped us to be more efficient. We are also way more comfortable with React.

Outside from time-related considerations, we are more and more convinced that working with React was a smart choice to conceive user interfaces. It may be obvious for you, but at first glance, it wasn’t for us until our Le lab sessions showed us the way. Combined with Redux, our code readability and maintainability increased drastically. On that note, if you are writing Redux/React applications and/or you are interested in Franklin, we would love to hear about you. Drop us a note by email or on Twitter!


Wanna test the new Franklin?

Yes, show me this now!