Oct 24, 2008

How to create a view to add an object...

How to create a view to add an object with a PointField

All the files described below are part of a django app called dj_cartographe. The objective of this short wiki page is to help you to create a web page where you will be able to add object RunningWaterOutage.

Let us take the following models that represents a simplified version of what could be a RunningWaterOutage :


# dj_cartographe/models.py
from django.contrib.gis.db import models

class RunningWaterOutage(models.Model):
"""A spatial model for interesting locations """
name = models.CharField(max_length=50, )
description = models.TextField()
creation_date = models.DateTimeField(auto_now_add=True)
start_date = models.DateTimeField()
end_date = models.DateTimeField()

geometry = models.PointField(srid=4326) #EPSG:4236 is the spatial reference for our data
objects = models.GeoManager() # so we can use spatial queryset methods

def __unicode__(self): return self.name


In order to use this model in django admin you should configure it as follow :


# dj_cartographe/admins.py
from django.contrib.gis import admin
from django.contrib.gis.maps.google import GoogleMap
from dj_cartographe.models import *
class RunningWaterOutageAdminOptions(admin.OSMGeoAdmin):
list_display = ('name', 'description', 'start_date', 'end_date')
list_filter = ('name', 'description', 'start_date', 'end_date')
fieldsets = (
('Location Attributes', {'fields': (('name', 'description', 'start_date', 'end_date',))}),
('Editable Map View', {'fields': ('geometry',)}),
)
# Default GeoDjango OpenLayers map options
scrollable = False
map_width = 700
map_height = 325
GMAP = GoogleMap(key='<YOUR GOOGLE KEY THERE>') # Can also set GOOGLE_MAPS_API_KEY in settings
class RunningWaterOutageGoogleAdminOptions(admin.OSMGeoAdmin):
extra_js = [GMAP.api_url + GMAP.key]
map_template = 'gis/admin/google.html'
list_display = ('name', 'description', 'start_date', 'end_date')
list_filter = ('name', 'description', 'start_date', 'end_date')
fieldsets = (
('Location Attributes', {'fields': (('name', 'description', 'start_date', 'end_date',))}),
('Editable Map View', {'fields': ('geometry',)}),
)
# Default GeoDjango OpenLayers map options
scrollable = False
map_width = 700
map_height = 325
# Register our model and admin options with the admin site
admin.site.register(RunningWaterOutage, RunningWaterOutageAdminOptions)
# Register the google enabled admin site
google_admin = admin.AdminSite()
google_admin.register(RunningWaterOutage, RunningWaterOutageGoogleAdminOptions)


It is interesting to note that in the example above we have created in fact 2 instances of admin site.

Now the interesting part of this recipe, you will find below a method that will enable you to provide a map as user interface to enter the location of the RunningWaterOutage.


#dj_cartographe/forms.py
from django.forms import ModelForm
from django.forms.fields import CharField
from django.contrib.gis.admin.options import GeoModelAdmin
from dj_cartographe.admin import google_admin

from dj_cartographe.models import RunningWaterOutage

geomodeladmin = GeoModelAdmin(RunningWaterOutage, google_admin)
db_field = RunningWaterOutage._meta.get_field('geometry')

class RunningWaterOutageForm(ModelForm):
# Overiding the default Field type
geometry = CharField(widget=geomodeladmin.get_map_widget(db_field))
class Meta:
model = RunningWaterOutage

class Media:
js = (
"http://openlayers.org/api/2.6/OpenLayers.js",
)


This RunningWaterOutageForm will contain when rendered on a template a nice OpenLayers map that your users will be able to use instead of having to key in the Point in a textarea.

Now the template :

# dj_cartographe/templates/running_water_outage_edit.html
{% extends "base.html" %}

{% block title %}my first map{% endblock %}
{% block media %}
{{ form.media }}
{% endblock %}

{% block content %}
<h1>my first map</h1>
<form action="." method="POST">
{{form}}
<input type="submit" value="Submit" />
</form>
{% endblock %}


There is nothing special there, however it is interesting to note that we use {{ form.media }} to pull in the javascript.



The last piece is to create the view :



#dj_cartographe/views.py
from django.shortcuts import render_to_response
from dj_cartographe.models import RunningWaterOutage
from dj_cartographe.forms import RunningWaterOutageForm


def running_water_outage_add(request):

if request.method == "POST":
running_water_outage_form = RunningWaterOutageForm(request.POST)
if running_water_outage_form.is_valid():
running_water_outage_form.save()
else:
running_water_outage_form = RunningWaterOutageForm()

return render_to_response("running_water_outage_edit.html",
{"form" : running_water_outage_form,
})





Here it is the result of this small recipe :

Jul 25, 2008

How to evaluate the coverage of a django test suite

I am using this recipe to estimate the coverage of the test suites arounds my django's projects.

Software Prerequisites


In order to follow this recipe you will need to have the following software installed :
  • Django
  • python (obviously)
  • coverage.py
I will not explain how to get the first 2 items of this list installed since if you are reading this I am assuming that you are familiar with them. "Coverage" is a bit different, python package index make installing this component a piece of cake.

# easy_install coverage


Usage of coverage with django test suite

This presentation give a recipe to evaluate the coverage of your test suite. I found this page useful to understand how to use it. If you are looking for a project to test this recipe on you can checkout "django-survey". The 3 lines below is all you need to get a report on the coverage of your test suite.
# coverage.py -e (1)
# coverage.py -x manage.py test survey (2)
# coverage.py -r -m >report.txt (3)

The first line erases collected coverage data, the second executes the module and collect the coverage data, the third line reports on the statement coverage for the given files and show line numbers of the statements that weren't executed.

Then you need to analyze the file called "report.txt" and extract the information usefull to your project. In our case all the files located in django-survey :


Name
------------------------------------------------------------------------------------------------------------------------------------------------
__init__ 0 0 100%
[...]
c:\yml\_myscript_\dj_things\web_development\django-survey\survey\__init__ 0 0 100%
c:\yml\_myscript_\dj_things\web_development\django-survey\survey\forms 172 117 68% 32, 47-49, 56, 74-86, 96, 107, 117-120, 126-136, 139-145, 147-159, 212, 226-227, 230
c:\yml\_myscript_\dj_things\web_development\django-survey\survey\models 157 142 90% 28-29, 70, 82-83, 88-89, 102-104, 109, 126, 159, 211, 236
c:\yml\_myscript_\dj_things\web_development\django-survey\survey\templatetags\__init__ 0 0 100%
c:\yml\_myscript_\dj_things\web_development\django-survey\survey\templatetags\survey 10 7 70% 8, 16-17
c:\yml\_myscript_\dj_things\web_development\django-survey\survey\tests\__init__ 2 2 100%
c:\yml\_myscript_\dj_things\web_development\django-survey\survey\tests\test_images 1 1 100%
c:\yml\_myscript_\dj_things\web_development\django-survey\survey\tests\test_models 1 1 100%
c:\yml\_myscript_\dj_things\web_development\django-survey\survey\tests\test_urls 1 1 100%
c:\yml\_myscript_\dj_things\web_development\django-survey\survey\urls 6 6 100%
c:\yml\_myscript_\dj_things\web_development\django-survey\survey\views 183 126 68% 47, 54-65, 79-83, 90, 94, 96, 124-125, 150-151, 157-158, 183-186, 230-231, 246, 261-263, 280-281, 311-313, 328, 341-342, 359-360, 379, 419-425, 444-456, 462-474
[...]
manage 13 9 69% 10-13
management\__init__ 0 0 100%
settings 31 31 100%
urls 4 4 100%


Dec 29, 2007

How to use VOIP service with a N95

Recently I had the occasion to spend some time with a N95. For those of you that doesn't know what is a N95 they can go there to learn more about it. To put it in a nutshell it is a beautiful piece of technology among many things you can use it as a phone, camera, browser, GPS.

After sometimes I noticed that something important to me was missing the voice over IP. A mobile phone with the wifi is a call for this feature, isn't it? My personal use case for this is while sitting in a restaurant, café, hotel with a free or cheap hot-spot in a foreign country I would like to call some friends using Skype for example. I will see in the following part of this article that Skype is not the only option.

Let us come to the facts:

What do you need?
  1. N95 obviously this is the phone I will use however it is interesting to note that this technique can be applied to many other mobiles
  2. An internet connection
  3. wifi network
  4. a computer with the Nokia PC suite

Step by step

The corner stone of this capability is a software called : fring. Here it is the definition that you can find on their web site:


fring™ is a free mobile VoIP application that utilizes
free WiFi or your mobile internet data plan to make free mobile
internet calls and live chat (IM) to other ‘fringsters’ and PC-based
services including Skype®, Google Talk™, ICQ, MSN®
Messenger, Twitter, AIM®
&Yahoo!TM **


You can download this amazing piece of software for free on their web site. The installation procedure is very easy also I have chosen to download the software on my laptop and then to install it from there using the Nokia PC Suite.

Once fring is installed on you mobile you are almost set the last things you need to do is to configure your skype account. You have to enter your username/password in "Option/Configure Service".

That is it you are now able to call you contacts on skype...

Enjoy the liberty given by this new way of communicating give me a fring if you like it :-))



Happy new year.

--yml


Dec 16, 2007

Django internationalisation

Multilingual web site with django

I have been recently working on a web application that needs to support several languages, English and French among others. This web application is built on top of Django which has a nice built in support for internationalisation, also known as i18n. However along the road of building this web application I have noticed some gaps between what was existing and what I was trying to achieve.

Here it is the list whithout any particular order:
- a multilingual flatpages
- a way to explicitely use display the language code (ie fr for French and en for English) directly in the URL. I do prefer http://yml.alwaysdata.net/fr/help instead of http://yml.alwaysdata.net/ and rely on the browser settings to direct my user the right page. In addition I have the feeling that this would result in a much better indexation by search engine like Google or Yahoo, ...
- a simple UI to set the language.

The good news is that after some time, search and chat on IRC I have found a way to close all of them.

Multilingual flatpages
This has been amazingly simple thanks to django-multilingual, a library kindly developped by ???. In a matter of one or two hours I have forked "django.contrib.flatpages" and adapted it to support my specific needs. The new django application is name django multilingual and is available there . This might be integrated as battery inside django-multilingual library in the future. This application is fairly simple and intuitive to use once it is installed you can start adding you multilingual flatpages directly in "admin". For each multilingual flatpage you will be able to write a title and a content per language in my case french and english. You can see this application live there : http://yml.alwaysdata.net

Language code explicit in the URL

I found a nice post there presenting a solution closing that gap, unfortunately I am unable to find the link again. The proposal is to add a middleware that intercept the request and proceed to the following operations:
* detect the language code in the URL
* Force the language to the one specified in the URL
* remove the language code from the URL this allows you to use your urls.py unchanged

[code] --- with the indentation

from django.utils.cache import patch_vary_headers
from django.utils import translation

class MultilingualURLMiddleware:
def get_language_from_request (self,request):
from django.conf import settings
import re
supported = dict(settings.LANGUAGES)
lang = settings.LANGUAGE_CODE[:2]
check = re.match(r"/(\w\w)/.*", request.path)
changed = False
if check is not None:
request.path = request.path[3:]
t = check.group(1)
if t in supported:
lang = t
if hasattr(request, "session"):
request.session["django_language"] = lang
else:
response.set_cookie("django_language", lang)
changed = True
if not changed:
if hasattr(request, "session"):
lang = request.session.get("django_language", None)
if lang in supported and lang is not None:
return lang
else:
lang = request.COOKIES.get("django_language", None)
if lang in supported and lang is not None:
return lang
return lang
def process_request(self, request):
from django.conf import settings
language = self.get_language_from_request(request)
if language is None:
language = settings.LANGUAGE_CODE[:2]
translation.activate(language)
request.LANGUAGE_CODE = translation.get_language()
def process_response(self, request, response):
patch_vary_headers(response, ("Accept-Language",))
translation.deactivate()
return response

[/code]

A simple UI to select the language

As of today I am using a set of dynamicaly generated URLs but I might change this in the future to use a form instead. This is fairly simple as soon as you know all the pieces. :-)

[code]

{%trans "Set your language" %}


|
{% for language in LANGUAGES %}
{% ifequal LANGUAGE_CODE language.0 %}
{{language.1}}|
{% else %}
{{ language.1 }}|
{% endifequal %}
{% endfor %}

[/code]

As you have seen most of bricks I had to use to implement my multilingual web application were available out there on the internet. After all multilingual web application is not something new, but I was a bit surprised not to find any ready to use library suiting my requirements. Several reasons could explain this:
* I had some very unique requirements
* What I did was so simple that my expectation was to high
* I missed something important
* I should not have done it that way

I would be glad hear your opinion if you have one. I am sure that the solution presented there can be enhanced do not hesitate to post a comment on that blog to let me know how. :-)

PS1: You are free to copy, modify the code posted on this blog.
PS2: Does someone know how to fix the code indentation on blogger.com?

Dec 12, 2007

Hello World

I know this title is terrible but I am sorry, I am not finding anything better for my first post on this Blog. I don't know how often I will post there but a couple of time in the past I was willing to Express/Shout an opinion and I was enable to do it.
I guess that at the beginning I will use it mainly to do some marketing around a web application enabling focusing on making the process of publishing an Online Survey easy.

This application can be found there : http://yml.alwaysdata.net
I will write more about it in my next post.