A command line installer script for opencart and why command line scripts rock!

We have been using command line scripts and tools since long but it’s only recently that we discovered how much they can help productivity.

We are actively working on our product KodeCRM and a part of the work is developing, maintaining and releasing updates to modules/extensions for integration with different opensource ecommerce platforms such as OpencartMagento etc. During the last few weeks one glaring pain point we have found in this entire process is that of quickly setting up environments for testing and debugging whenever an update is made to the extensions (for various reasons such as bug fixes, support for newer versions of the platforms or api changes) and it needs to be tested on multiple versions of multiple platforms for compatibility just before releasing.

One thing that most of the OS platforms that we develop modules for have in common is a web based installation wizard. Being written in PHP, all the user needs to do is drop the files somewhere inside the document root and point the browser to the url. The installation wizard shows up for the first time and guides the user through a sequence of clicks and literally installs itself.

Now this is awesome, particularly for a non-technical user trying to do a DIY installation for evaluating their options. But for developers like us, who need to do it many times, it means a lot of clicks and frustration! In this case it’s significantly faster to use command line than the GUI. Because where there is repetition of tasks, the ability to record actions comes in handy. And command line interface inherently supports recording of actions to some extent by means of scripts.

To alleviate the pain, we have written a command line installer script for opencart that takes some arguments and sets up an instance of Opencart locally. Such a script already existed for Magento which is really where we got the inspiration from for writing one for Opencart.

To automate further, there are simple shell scripts written on top of these installer scripts. They take care of things like downloading the platform files, moving them to the document root of the local server, ensuring that certain files have sufficient permissions, setting up the database, adding test data to the database and finally adding the KodeCRM extension files. After all this is done the script spits out some links to the console. All that’s left to be done now is to open them in a browser and start testing. In future an interesting experiment would be to take it to the next level by automating the testing part too by using Selenium.

Talking of repeated tasks there is one more thing that’s automated using scripts which is creating a release or zip package of the files so that the extension can be hosted for download.

All these tools have been of immense help to us and it’s hard to imagine now how we could live without them earlier. So if you are a developer, it can’t be stressed enough that you should automate day-to-day tasks by writing command line scripts. It really goes without saying that your deployment process must be automated. If you don’t know shell scripting, learn some basics or use whichever scripting language you are familiar with eg. python, php, ruby, perl etc.

All our scripts mentioned above are open source and can be found in the git repositories of the respective modules on github. The Opencart command line installer can be found here. We opened a pull request to the main Opencart project on github and are super happy that it’s been merged into the main project (Yay! :) )

For more info and updates about opensource code released by us, check out our organization page on github

Using custom middleware to load user prefered language in Django

Update: With increase in traffic, this method started to bite us so we are no longer
using this approach. The problem was that since we have set django to store session
data in database it resulted in huge number of read and write queries on each request.
Using internationalized url patterns is a much better approach. However, the example
shown in this blog post is still valid for learning about django middleware.

Django has great support for Internationalization. For this, it depends on a builtin middleware to discover the language preference and then shows the translated version of the web page to the user. For localization, gettext is used which is quite simple and convenient to work with.

In this article, we will see how to load user prefered language in Django which makes a pretty good real world use case of middlewares. The example is directly taken from the code base of our product KodeCRM. If you are new to django or haven’t had the
opportunity to play with middleware classes yet, hopefully you will learn something new.

The problem

In KodeCRM, users can choose to show the chat widget on their site in the language of their choice. The language preferences are stored in mongo documents along with other settings. We need to load the chat widget for different users in the languages they have selected.

The naive solution

The most straightforward approach of implementing something like this would be as follows

1. In the view that you wish to internationalize, identify the user from the request.

2. Get the language preference of the user.

3. Set the language code in session object which is one of the places that django discovers the language preference from.

1 2 3 4 5 6
def chat(request, appid):
store = get_object_or_404(Store, appid=appid)
customizations = get_customization(store)
request.session['django_language'] = customizations['language']
# ...
# ...
view raw views.py This Gist brought to you by GitHub.

But there is a bug in there. If you try to run it, you will notice that when the chat widget is loaded for the first time, all text appears in English (default language). On the subsequent refreshes though it appears in the correct language.

From this we can only guess that for the first request, the language code is not set in session for some reason and in the later requests, it is. But in order to get to the root of the problem we need to understand how middlewares work and to study the LocaleMiddleware in particular which powers the language preference discovery part.

The best place to read about middlewares is the official django documentation which is awesome. If you are new to middlewares, it’s recommended that you read it right now and come back while I shall wait :-)

Ok, with middlewares understood let’s move to the LocaleMiddleware and the best place to read about the LocaleMiddleware is the source code. Without getting much into details, we will focus on the process_request method of the middleware object which is called on
every request before the view function is executed.

Here the task of actually discoverying the language is delegated to other functions but in gist this function sets the language for the request. The problem with the above implementation is that in the first request, the LocaleMiddleware discovers default language before our view is executed and so our code of setting the user prefered
language in session is kind of useless because the language for the request is already set. Hence the text in the first response appears in English (default language). In the next request however, the session is set and everything works fine.

So how do we fix it?

One way is to do what the LocaleMiddleware does in the view again. But this approach is not elegant.

Enter custom middleware

A better approach is to write our own middleware that will set the correct language in the session object ahead of the LocalMiddleware discovering it. Writing a custom middleware in django can’t be any more easier.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
class ChatLocaleMiddleware(object):
 
def process_request(self, request):
if request.path in ['/jsi18n/']:
return None
match = SHOPPER_CHAT_PATH.match(request.path)
if match is not None:
appid = match.groups()[0]
try:
store = Store.objects.get(appid=appid)
except Store.DoesNotExist:
return HttpResponse(status=404)
customizations = get_customizations(store)
request.session['django_language'] = customizations['language']
request.store = store
request.customizations = customizations
else:
request.session['django_language'] = settings.LANGUAGE_CODE
view raw middleware.py This Gist brought to you by GitHub.

Don’t forget to add the middleware in settings module before LocaleMiddleware.

1 2 3 4 5 6
MIDDLEWARE_CLASSES = (
# ...
'shopper.middleware.ChatLocaleMiddleware',
'django.middleware.locale.LocaleMiddleware',
# ...
)
view raw settings.py This Gist brought to you by GitHub.

You may have noticed, the above code also takes care of a few other things besides setting the language which are:

1. We need to set the language in session only for one particular view (chat widget) and not for the entire django application. This is done by doing a regex matching of request path against the path desired.

2. For the request path ‘/jsi18n/’, the LocaleMiddleware can take the language thats already in session so no pre-processing is required for this request and we just let it go!

3. In the view we need to access the variables “store“ and “customizations“. By adding them as attributes to the request object we can prevent querying the databases again.

That’s it. With this middleware in place, the correct language will load all the time for the specific view. Hope it helps anyone who’s trying to wrap their minds around middlewares in django. Feel free to share a different approach or any criticism you may have for the approach discussed through comments.