merged with sms app loaded
This commit is contained in:
commit
fad6095808
|
@ -51,7 +51,7 @@ class FareAdmin(admin.ModelAdmin):
|
||||||
class UniqueRouteAdmin(admin.ModelAdmin):
|
class UniqueRouteAdmin(admin.ModelAdmin):
|
||||||
list_display = ("route","from_stop", "to_stop","distance","is_full")
|
list_display = ("route","from_stop", "to_stop","distance","is_full")
|
||||||
readonly_fields = ("route","from_stop", "to_stop","distance","is_full")
|
readonly_fields = ("route","from_stop", "to_stop","distance","is_full")
|
||||||
search_fields = ("route", "from_stop__name", "to_stop__name")
|
search_fields = ("route__alias", "from_stop__name", "to_stop__name")
|
||||||
ordering = ('route',)
|
ordering = ('route',)
|
||||||
list_per_page = 50
|
list_per_page = 50
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ from ox.django.shortcuts import get_object_or_404_json, render_to_json_response
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
import json
|
import json
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
import re
|
||||||
|
|
||||||
def route(request, slug):
|
def route(request, slug):
|
||||||
srid = int(request.GET.get("srid", 4326))
|
srid = int(request.GET.get("srid", 4326))
|
||||||
|
@ -28,29 +29,46 @@ def area(request, slug):
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
def routes(request):
|
def routes(request):
|
||||||
qset = Route.objects.all()
|
q = request.GET.get("q", "")
|
||||||
if request.GET.has_key('q'):
|
in_regex = re.compile(r'(\d{1,3})') # used to extract the route number string out of the query string - for eg, gets "21" from "21Ltd"
|
||||||
q = request.GET.get('q', '')
|
match = re.findall(in_regex, q)
|
||||||
qset = qset.filter(alias__icontains=q) #FIXME: make a better Q object
|
if match:
|
||||||
routes = [route.alias for route in qset]
|
route_no = match[0]
|
||||||
return render_to_json_response(routes)
|
else:
|
||||||
|
route_no = ''
|
||||||
|
ret = []
|
||||||
|
if route_no != '':
|
||||||
|
out_regex = re.compile(r'.*(\D|\A)%s(\D|\Z).*' % route_no) # used for, for eg. to filter out '210Ltd' when user searches for '21'. Checks for non-digit or start of string, followed by route_no, followed by non-digit or end of string
|
||||||
|
qset = Route.objects.filter(alias__icontains=route_no)
|
||||||
|
for route in qset:
|
||||||
|
if re.match(out_regex, route.alias):
|
||||||
|
ret.append(route.alias)
|
||||||
|
else:
|
||||||
|
qset = Route.objects.all()
|
||||||
|
for route in qset:
|
||||||
|
ret.append(route.alias)
|
||||||
|
# routes = [route.alias for route in qset]
|
||||||
|
return render_to_json_response(ret)
|
||||||
|
|
||||||
|
|
||||||
def areas(request):
|
def areas(request):
|
||||||
qset = Area.objects.all()
|
q = request.GET.get("q", "")
|
||||||
if request.GET.has_key('q'):
|
if q != '':
|
||||||
q = request.GET.get('q', '')
|
qset = Area.objects.find_approximate(q, 0.33)
|
||||||
qset = qset.filter(display_name__icontains=q)
|
else:
|
||||||
|
qset = Area.objects.all()
|
||||||
areas = [area.slug for area in qset]
|
areas = [area.slug for area in qset]
|
||||||
return render_to_json_response(areas)
|
return render_to_json_response(areas)
|
||||||
|
|
||||||
|
|
||||||
def stops(request):
|
def stops(request):
|
||||||
qset = Stop.objects.all()
|
q = request.GET.get("q", "")
|
||||||
srid = int(request.GET.get("srid", 4326))
|
if q != '':
|
||||||
if request.GET.has_key('q'):
|
qset = Stop.objects.find_approximate(q, 0.33)
|
||||||
q = request.GET.get('q', '')
|
else:
|
||||||
qset = qset.filter(display_name__icontains=q) #FIXME: This definitely needs to be a Q object with OR lookups for area name, road name, etc.
|
qset = Stop.objects.all()
|
||||||
|
srid = int(request.GET.get("srid", 4326))
|
||||||
return render_to_json_response({
|
return render_to_json_response({
|
||||||
'type': 'FeatureCollection',
|
'type': 'FeatureCollection',
|
||||||
'features': [stop.get_geojson(srid=srid) for stop in qset]
|
'features': [stop.get_geojson(srid=srid) for stop in qset]
|
||||||
|
|
0
chaloBEST/mumbai/management/__init__.py
Normal file
0
chaloBEST/mumbai/management/__init__.py
Normal file
0
chaloBEST/mumbai/management/commands/__init__.py
Normal file
0
chaloBEST/mumbai/management/commands/__init__.py
Normal file
20
chaloBEST/mumbai/management/commands/trgmidx.py
Normal file
20
chaloBEST/mumbai/management/commands/trgmidx.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
from django.db import connection
|
||||||
|
from mumbai import models
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = "Instantiates the pg_trgm indexes"
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
cursor = connection.cursor()
|
||||||
|
for name in dir(models):
|
||||||
|
model = getattr(models, name)
|
||||||
|
if not hasattr(model, "objects") or \
|
||||||
|
not isinstance(model.objects, models.TrigramSearchManager):
|
||||||
|
continue
|
||||||
|
table = model._meta.db_table
|
||||||
|
for column in model.objects.trigram_columns:
|
||||||
|
sql = """CREATE INDEX %s_%s_trgm_idx ON %s USING gin (%s gin_trgm_ops);""" % (
|
||||||
|
table, column, table, column)
|
||||||
|
cursor.execute(sql)
|
||||||
|
cursor.execute("COMMIT;")
|
|
@ -3,6 +3,7 @@ from django.contrib.gis.geos import Point
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.contrib.contenttypes import generic
|
from django.contrib.contenttypes import generic
|
||||||
|
from django.db import connection
|
||||||
import json
|
import json
|
||||||
|
|
||||||
STOP_CHOICES = ( ('U','Up'),
|
STOP_CHOICES = ( ('U','Up'),
|
||||||
|
@ -42,7 +43,32 @@ SCHED = {
|
||||||
'2nd &4th':['???']
|
'2nd &4th':['???']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TrigramSearchManager(models.Manager):
|
||||||
|
def __init__(self, trigram_columns=[]):
|
||||||
|
super(TrigramSearchManager, self).__init__()
|
||||||
|
self.trigram_columns = trigram_columns
|
||||||
|
|
||||||
|
def set_threshold(self, threshold):
|
||||||
|
"""Set the limit for trigram similarity matching."""
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.execute("""SELECT set_limit(%f)""" % threshold)
|
||||||
|
|
||||||
|
def find_approximate(self, text, match=0.5):
|
||||||
|
self.set_threshold(match)
|
||||||
|
similarity_measure = "greatest(%s)" % ",".join(["similarity(%s, %%s)" % col for col in self.trigram_columns])
|
||||||
|
similarity_filter = " OR ".join(["%s %%%% %%s" % col for col in self.trigram_columns])
|
||||||
|
text_values = [text] * len(self.trigram_columns)
|
||||||
|
qset = self.get_query_set()
|
||||||
|
# use the pg_trgm index via the % operator
|
||||||
|
qset = qset.extra(select={"similarity":similarity_measure},
|
||||||
|
select_params=text_values,
|
||||||
|
where=[similarity_filter],
|
||||||
|
params=text_values,
|
||||||
|
order_by=["-similarity"])
|
||||||
|
return qset
|
||||||
|
|
||||||
class Area(models.Model):
|
class Area(models.Model):
|
||||||
|
objects = TrigramSearchManager(("name", "name_mr", "display_name"))
|
||||||
code = models.IntegerField() #primary_key=True)
|
code = models.IntegerField() #primary_key=True)
|
||||||
slug = models.SlugField(null=True)
|
slug = models.SlugField(null=True)
|
||||||
name = models.TextField(blank=True, max_length=255)
|
name = models.TextField(blank=True, max_length=255)
|
||||||
|
@ -91,8 +117,8 @@ class Fare(models.Model):
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return str(self.slab)
|
return str(self.slab)
|
||||||
|
|
||||||
|
|
||||||
class Stop(models.Model):
|
class Stop(models.Model):
|
||||||
|
objects = TrigramSearchManager(("name", "name_mr", "display_name"))
|
||||||
code = models.IntegerField()
|
code = models.IntegerField()
|
||||||
slug = models.SlugField(null=True)
|
slug = models.SlugField(null=True)
|
||||||
name = models.TextField(blank=True, max_length=255)
|
name = models.TextField(blank=True, max_length=255)
|
||||||
|
@ -123,7 +149,7 @@ class Stop(models.Model):
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_geojson(self, srid=4326):
|
def get_geojson(self, srid=4326):
|
||||||
print srid
|
# print srid
|
||||||
if self.point is not None:
|
if self.point is not None:
|
||||||
geom = json.loads(self.point.transform(srid, True).geojson)
|
geom = json.loads(self.point.transform(srid, True).geojson)
|
||||||
else:
|
else:
|
||||||
|
@ -178,7 +204,7 @@ class Stop(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Route(models.Model):
|
class Route(models.Model):
|
||||||
code = models.TextField(max_length=255, unique=True)
|
code = models.TextField(max_length=255, unique=True) #FIXME: Why is this a TextField??
|
||||||
slug = models.SlugField(null=True)
|
slug = models.SlugField(null=True)
|
||||||
alias = models.TextField(max_length=255)
|
alias = models.TextField(max_length=255)
|
||||||
from_stop_txt = models.TextField(max_length=500)
|
from_stop_txt = models.TextField(max_length=500)
|
||||||
|
@ -188,6 +214,9 @@ class Route(models.Model):
|
||||||
distance = models.DecimalField(max_digits=3, decimal_places=1)
|
distance = models.DecimalField(max_digits=3, decimal_places=1)
|
||||||
stages = models.IntegerField()
|
stages = models.IntegerField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['code']
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return "/route/%s/" % self.alias
|
return "/route/%s/" % self.alias
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ var API_BASE = "/1.0/",
|
||||||
var $li = $('<div />')
|
var $li = $('<div />')
|
||||||
.addClass("listItem")
|
.addClass("listItem")
|
||||||
.appendTo($list);
|
.appendTo($list);
|
||||||
var $txt = $('<span >').addClass("listItemText").text(v).appendTo($li);
|
var $txt = $('<span />').addClass("listItemText").text(v).appendTo($li);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@ var API_BASE = "/1.0/",
|
||||||
$('.selectedListItem').find(".stopsList").hide().remove();
|
$('.selectedListItem').find(".stopsList").hide().remove();
|
||||||
$('.selectedListItem').removeClass("selectedListItem");
|
$('.selectedListItem').removeClass("selectedListItem");
|
||||||
$target.addClass("selectedListItem");
|
$target.addClass("selectedListItem");
|
||||||
|
|
||||||
if ($target.data("hasList")) {
|
if ($target.data("hasList")) {
|
||||||
var $stopsList = $target.find(".stopsList");
|
var $stopsList = $target.find(".stopsList");
|
||||||
$stopsList.slideDown();
|
$stopsList.slideDown();
|
||||||
|
@ -223,12 +224,15 @@ var API_BASE = "/1.0/",
|
||||||
var geojsonString = JSON.stringify(geojson);
|
var geojsonString = JSON.stringify(geojson);
|
||||||
//console.log(geojsonString);
|
//console.log(geojsonString);
|
||||||
var url = API_BASE + "stop/" + stop.slug + "?srid=3857";
|
var url = API_BASE + "stop/" + stop.slug + "?srid=3857";
|
||||||
$.post(url, {'geojson': geojsonString}, function(response) {
|
var $postXHR = $.post(url, {'geojson': geojsonString}, function(response) {
|
||||||
if (response.errors) {
|
if (response.errors) {
|
||||||
alert("error saving");
|
alert("error saving");
|
||||||
}
|
}
|
||||||
//console.log(response);
|
//console.log(response);
|
||||||
}, "json");
|
}, "json");
|
||||||
|
$postXHR.fail(function(e) {
|
||||||
|
alert('failed ' + JSON.stringify(e));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return $div;
|
return $div;
|
||||||
}
|
}
|
||||||
|
@ -318,6 +322,7 @@ var API_BASE = "/1.0/",
|
||||||
// console.log(e.feature);
|
// console.log(e.feature);
|
||||||
var slug = e.feature.attributes.slug;
|
var slug = e.feature.attributes.slug;
|
||||||
//alert("selected " + slug);
|
//alert("selected " + slug);
|
||||||
|
$('.selectedStop').removeClass("selectedStop");
|
||||||
highlightStop(slug);
|
highlightStop(slug);
|
||||||
var stop = e.feature.attributes;
|
var stop = e.feature.attributes;
|
||||||
var geom = {
|
var geom = {
|
||||||
|
|
11
gateway/README
Normal file
11
gateway/README
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
These files make the SMS gateway on atlas go.
|
||||||
|
|
||||||
|
* apt-get install supervisor
|
||||||
|
* Create a virtualenv in /srv/smsBEST
|
||||||
|
* Install RapidSMS to the virtualenv:
|
||||||
|
http://github.com/schuyler/rapidsms
|
||||||
|
* rapidsms-admin.py startproject gateway
|
||||||
|
* Put settings.py in /srv/smsBEST/gateway
|
||||||
|
* Run manage.py syncdb in /srv/smsBEST/gateway
|
||||||
|
* Put run_gateway.sh in /srv/smsBEST/bin
|
||||||
|
* Put smsBEST.conf in /etc/supervisor/conf.d
|
9
gateway/run_gateway.sh
Executable file
9
gateway/run_gateway.sh
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Launch the router. Put this file in /srv/smsBEST/bin.
|
||||||
|
#
|
||||||
|
cd /srv/smsBEST
|
||||||
|
source bin/activate
|
||||||
|
cd gateway
|
||||||
|
python manage.py runrouter
|
||||||
|
|
197
gateway/settings.py
Executable file
197
gateway/settings.py
Executable file
|
@ -0,0 +1,197 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# vim: ai ts=4 sts=4 et sw=4
|
||||||
|
# encoding=utf-8
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------- #
|
||||||
|
# MAIN CONFIGURATION #
|
||||||
|
# -------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
# you should configure your database here before doing any real work.
|
||||||
|
# see: http://docs.djangoproject.com/en/dev/ref/settings/#databases
|
||||||
|
DATABASES = {
|
||||||
|
"default": {
|
||||||
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
|
"NAME": "rapidsms.sqlite3",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# the rapidsms backend configuration is designed to resemble django's
|
||||||
|
# database configuration, as a nested dict of (name, configuration).
|
||||||
|
#
|
||||||
|
# the ENGINE option specifies the module of the backend; the most common
|
||||||
|
# backend types (for a GSM modem or an SMPP server) are bundled with
|
||||||
|
# rapidsms, but you may choose to write your own.
|
||||||
|
#
|
||||||
|
# all other options are passed to the Backend when it is instantiated,
|
||||||
|
# to configure it. see the documentation in those modules for a list of
|
||||||
|
# the valid options for each.
|
||||||
|
INSTALLED_BACKENDS = {
|
||||||
|
"modem": {
|
||||||
|
"ENGINE": "rapidsms.backends.gsm",
|
||||||
|
"PORT": "/dev/ttyS0",
|
||||||
|
"baudrate": 115200,
|
||||||
|
"timeout": 10
|
||||||
|
},
|
||||||
|
#"verizon": {
|
||||||
|
# "ENGINE": "rapidsms.backends.gsm,
|
||||||
|
# "PORT": "/dev/ttyUSB1"
|
||||||
|
#},
|
||||||
|
"message_tester": {
|
||||||
|
"ENGINE": "rapidsms.backends.bucket",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GATEWAY = {
|
||||||
|
"backend": "modem",
|
||||||
|
"secret": "something secret",
|
||||||
|
"push": "http://chalobest.in:8086/?from=%(from)s&txt=%(txt)s&secret=%(secret)s"
|
||||||
|
}
|
||||||
|
|
||||||
|
AJAX_PROXY_HOST = "0.0.0.0" # to open the gateway from the outside
|
||||||
|
|
||||||
|
# to help you get started quickly, many django/rapidsms apps are enabled
|
||||||
|
# by default. you may wish to remove some and/or add your own.
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
|
||||||
|
# the essentials.
|
||||||
|
"django_nose",
|
||||||
|
"djtables",
|
||||||
|
"rapidsms",
|
||||||
|
|
||||||
|
# common dependencies (which don't clutter up the ui).
|
||||||
|
"rapidsms.contrib.handlers",
|
||||||
|
"rapidsms.contrib.ajax",
|
||||||
|
|
||||||
|
# enable the django admin using a little shim app (which includes
|
||||||
|
# the required urlpatterns), and a bunch of undocumented apps that
|
||||||
|
# the AdminSite seems to explode without.
|
||||||
|
"django.contrib.sites",
|
||||||
|
"django.contrib.auth",
|
||||||
|
"django.contrib.admin",
|
||||||
|
"django.contrib.sessions",
|
||||||
|
"django.contrib.contenttypes",
|
||||||
|
|
||||||
|
# the rapidsms contrib apps.
|
||||||
|
# "rapidsms.contrib.default",
|
||||||
|
"rapidsms.contrib.export",
|
||||||
|
"rapidsms.contrib.httptester",
|
||||||
|
# "rapidsms.contrib.locations",
|
||||||
|
"rapidsms.contrib.messagelog",
|
||||||
|
"rapidsms.contrib.messaging",
|
||||||
|
"rapidsms.contrib.gateway",
|
||||||
|
# "rapidsms.contrib.registration",
|
||||||
|
# "rapidsms.contrib.scheduler",
|
||||||
|
# "rapidsms.contrib.echo",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# this rapidsms-specific setting defines which views are linked by the
|
||||||
|
# tabbed navigation. when adding an app to INSTALLED_APPS, you may wish
|
||||||
|
# to add it here, also, to expose it in the rapidsms ui.
|
||||||
|
RAPIDSMS_TABS = [
|
||||||
|
("rapidsms.contrib.messagelog.views.message_log", "Message Log"),
|
||||||
|
# ("rapidsms.contrib.registration.views.registration", "Registration"),
|
||||||
|
("rapidsms.contrib.messaging.views.messaging", "Messaging"),
|
||||||
|
# ("rapidsms.contrib.locations.views.locations", "Map"),
|
||||||
|
# ("rapidsms.contrib.scheduler.views.index", "Event Scheduler"),
|
||||||
|
("rapidsms.contrib.httptester.views.generate_identity", "Message Tester"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------- #
|
||||||
|
# BORING CONFIGURATION #
|
||||||
|
# -------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
# debug mode is turned on as default, since rapidsms is under heavy
|
||||||
|
# development at the moment, and full stack traces are very useful
|
||||||
|
# when reporting bugs. don't forget to turn this off in production.
|
||||||
|
DEBUG = TEMPLATE_DEBUG = True
|
||||||
|
|
||||||
|
|
||||||
|
# after login (which is handled by django.contrib.auth), redirect to the
|
||||||
|
# dashboard rather than 'accounts/profile' (the default).
|
||||||
|
LOGIN_REDIRECT_URL = "/"
|
||||||
|
|
||||||
|
|
||||||
|
# use django-nose to run tests. rapidsms contains lots of packages and
|
||||||
|
# modules which django does not find automatically, and importing them
|
||||||
|
# all manually is tiresome and error-prone.
|
||||||
|
TEST_RUNNER = "django_nose.NoseTestSuiteRunner"
|
||||||
|
|
||||||
|
|
||||||
|
# for some reason this setting is blank in django's global_settings.py,
|
||||||
|
# but it is needed for static assets to be linkable.
|
||||||
|
MEDIA_URL = "/static/"
|
||||||
|
|
||||||
|
|
||||||
|
# this is required for the django.contrib.sites tests to run, but also
|
||||||
|
# not included in global_settings.py, and is almost always ``1``.
|
||||||
|
# see: http://docs.djangoproject.com/en/dev/ref/contrib/sites/
|
||||||
|
SITE_ID = 1
|
||||||
|
|
||||||
|
|
||||||
|
# the default log settings are very noisy.
|
||||||
|
LOG_LEVEL = "DEBUG"
|
||||||
|
LOG_FILE = "rapidsms.log"
|
||||||
|
LOG_FORMAT = "[%(name)s]: %(message)s"
|
||||||
|
LOG_SIZE = 8192 # 8192 bits = 8 kb
|
||||||
|
LOG_BACKUPS = 256 # number of logs to keep
|
||||||
|
|
||||||
|
|
||||||
|
# these weird dependencies should be handled by their respective apps,
|
||||||
|
# but they're not, so here they are. most of them are for django admin.
|
||||||
|
TEMPLATE_CONTEXT_PROCESSORS = [
|
||||||
|
"django.core.context_processors.auth",
|
||||||
|
"django.core.context_processors.debug",
|
||||||
|
"django.core.context_processors.i18n",
|
||||||
|
"django.core.context_processors.media",
|
||||||
|
"django.core.context_processors.request",
|
||||||
|
]
|
||||||
|
|
||||||
|
# template loaders load templates from various places.
|
||||||
|
# for djtables to work properly, the egg loader needs to be
|
||||||
|
# included, the others are fairly standard.
|
||||||
|
TEMPLATE_LOADERS = (
|
||||||
|
'django.template.loaders.filesystem.Loader',
|
||||||
|
'django.template.loaders.app_directories.Loader',
|
||||||
|
'django.template.loaders.eggs.Loader'
|
||||||
|
)
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------- #
|
||||||
|
# HERE BE DRAGONS! #
|
||||||
|
# these settings are pure hackery, and will go away soon #
|
||||||
|
# -------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
# these apps should not be started by rapidsms in your tests, however,
|
||||||
|
# the models and bootstrap will still be available through django.
|
||||||
|
TEST_EXCLUDED_APPS = [
|
||||||
|
"django.contrib.sessions",
|
||||||
|
"django.contrib.contenttypes",
|
||||||
|
"django.contrib.auth",
|
||||||
|
"rapidsms",
|
||||||
|
"rapidsms.contrib.ajax",
|
||||||
|
"rapidsms.contrib.httptester",
|
||||||
|
]
|
||||||
|
|
||||||
|
# the project-level url patterns
|
||||||
|
ROOT_URLCONF = "urls"
|
||||||
|
|
||||||
|
|
||||||
|
# since we might hit the database from any thread during testing, the
|
||||||
|
# in-memory sqlite database isn't sufficient. it spawns a separate
|
||||||
|
# virtual database for each thread, and syncdb is only called for the
|
||||||
|
# first. this leads to confusing "no such table" errors. We create
|
||||||
|
# a named temporary instance instead.
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if 'test' in sys.argv:
|
||||||
|
for db_name in DATABASES:
|
||||||
|
DATABASES[db_name]['TEST_NAME'] = os.path.join(
|
||||||
|
tempfile.gettempdir(),
|
||||||
|
"%s.rapidsms.test.sqlite3" % db_name)
|
5
gateway/smsBEST.conf
Normal file
5
gateway/smsBEST.conf
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# this file goes in /etc/supervisor/conf.d
|
||||||
|
[program:smsBEST]
|
||||||
|
command=/srv/smsBEST/bin/run_gateway.sh
|
||||||
|
directory=/srv/smsBEST/var
|
||||||
|
autorestart=true
|
|
@ -5,3 +5,5 @@
|
||||||
-e git+git://github.com/bit/django-extensions.git#egg=django_extensions
|
-e git+git://github.com/bit/django-extensions.git#egg=django_extensions
|
||||||
#django_extensions
|
#django_extensions
|
||||||
django-grappelli
|
django-grappelli
|
||||||
|
-e git+git://github.com/schuyler/rapidsms.git#egg=rapidsms
|
||||||
|
-e git+git://github.com/schuyler/arrest.git#egg=arrest
|
||||||
|
|
0
smsBEST/__init__.py
Normal file
0
smsBEST/__init__.py
Normal file
18
smsBEST/manage.py
Executable file
18
smsBEST/manage.py
Executable file
|
@ -0,0 +1,18 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# vim: ai ts=4 sts=4 et sw=4
|
||||||
|
|
||||||
|
#import sys, os
|
||||||
|
|
||||||
|
from django.core.management import execute_manager
|
||||||
|
import settings
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# project_root = os.path.abspath(
|
||||||
|
# os.path.dirname(__file__))
|
||||||
|
|
||||||
|
# path = os.path.join(project_root, "apps")
|
||||||
|
# sys.path.insert(0, path)
|
||||||
|
|
||||||
|
# sys.path.insert(0, project_root)
|
||||||
|
execute_manager(settings)
|
0
smsBEST/mumbai/__init__.py
Normal file
0
smsBEST/mumbai/__init__.py
Normal file
56
smsBEST/mumbai/app.py
Normal file
56
smsBEST/mumbai/app.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
from rapidsms.apps.base import AppBase
|
||||||
|
import re
|
||||||
|
import arrest
|
||||||
|
|
||||||
|
MAX_MSG_LEN = 160
|
||||||
|
DIGIT = re.compile(r"\d{1,3}")
|
||||||
|
PUNCT = re.compile(r"[^\w\s]")
|
||||||
|
"""
|
||||||
|
STYLE = {
|
||||||
|
"start": "-* ",
|
||||||
|
"repeat": " -*- ",
|
||||||
|
"end": " *-"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
STYLE = {"start": "", "repeat": "; ", "end": ""}
|
||||||
|
|
||||||
|
|
||||||
|
ChaloBest = arrest.Client("http://chalobest.in/1.0")
|
||||||
|
|
||||||
|
class App(AppBase):
|
||||||
|
def handle(self, msg):
|
||||||
|
if DIGIT.search(msg.text):
|
||||||
|
routes = ChaloBest.routes(q=msg.text.replace(" ", ""))
|
||||||
|
if not routes:
|
||||||
|
msg.respond("Sorry, we found no route marked '%s'." % msg.text)
|
||||||
|
return
|
||||||
|
detail = ChaloBest.route[routes[0]]
|
||||||
|
stops = detail['stops']['features']
|
||||||
|
origin, dest = stops[0]['properties'], stops[-1]['properties']
|
||||||
|
origin_name, dest_name = origin['display_name'], dest['display_name']
|
||||||
|
origin_area, dest_area = PUNCT.sub('', origin['area']), PUNCT.sub('', dest['area'])
|
||||||
|
msg.respond("%s: %s (%s) to %s (%s)" % (
|
||||||
|
",".join(routes), origin_name, origin_area, dest_name, dest_area))
|
||||||
|
else:
|
||||||
|
features = ChaloBest.stops(q=msg.text)['features']
|
||||||
|
if not features:
|
||||||
|
msg.respond("Sorry, we found no stops like '%s'." % msg.text)
|
||||||
|
return
|
||||||
|
stops = []
|
||||||
|
for feat in features:
|
||||||
|
stop = feat['properties']
|
||||||
|
if stops and stop["official_name"] == stops[-1]["official_name"]:
|
||||||
|
stops[-1]["routes"] += ", " + stop["routes"]
|
||||||
|
else:
|
||||||
|
stops.append(stop)
|
||||||
|
response = STYLE["start"]
|
||||||
|
for stop in stops:
|
||||||
|
match = stop["official_name"] + ": " + stop["routes"]
|
||||||
|
if len(response) > len(STYLE["repeat"]): response += STYLE["repeat"]
|
||||||
|
response += match
|
||||||
|
if len(response) > MAX_MSG_LEN: break
|
||||||
|
if len(response) > MAX_MSG_LEN:
|
||||||
|
response = response[:MAX_MSG_LEN-(len(STYLE["end"])+4)] + "..."
|
||||||
|
response += STYLE["end"]
|
||||||
|
msg.respond(response)
|
1
smsBEST/mumbai/views.py
Normal file
1
smsBEST/mumbai/views.py
Normal file
|
@ -0,0 +1 @@
|
||||||
|
# Create your views here.
|
205
smsBEST/settings.py
Normal file
205
smsBEST/settings.py
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# vim: ai ts=4 sts=4 et sw=4
|
||||||
|
# encoding=utf-8
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------- #
|
||||||
|
# MAIN CONFIGURATION #
|
||||||
|
# -------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
# you should configure your database here before doing any real work.
|
||||||
|
# see: http://docs.djangoproject.com/en/dev/ref/settings/#databases
|
||||||
|
DATABASES = {
|
||||||
|
"default": {
|
||||||
|
"ENGINE": "django.db.backends.postgresql_psycopg2",
|
||||||
|
"NAME": "smsbest",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# the rapidsms backend configuration is designed to resemble django's
|
||||||
|
# database configuration, as a nested dict of (name, configuration).
|
||||||
|
#
|
||||||
|
# the ENGINE option specifies the module of the backend; the most common
|
||||||
|
# backend types (for a GSM modem or an SMPP server) are bundled with
|
||||||
|
# rapidsms, but you may choose to write your own.
|
||||||
|
#
|
||||||
|
# all other options are passed to the Backend when it is instantiated,
|
||||||
|
# to configure it. see the documentation in those modules for a list of
|
||||||
|
# the valid options for each.
|
||||||
|
INSTALLED_BACKENDS = {
|
||||||
|
#"att": {
|
||||||
|
# "ENGINE": "rapidsms.backends.gsm",
|
||||||
|
# "PORT": "/dev/ttyUSB0"
|
||||||
|
#},
|
||||||
|
#"verizon": {
|
||||||
|
# "ENGINE": "rapidsms.backends.gsm,
|
||||||
|
# "PORT": "/dev/ttyUSB1"
|
||||||
|
#},
|
||||||
|
"atlas": {
|
||||||
|
"ENGINE": "rapidsms.backends.http",
|
||||||
|
"host": "0.0.0.0",
|
||||||
|
"port": 8086,
|
||||||
|
"gateway_url": "http://atlas.gnowledge.org:8001/gateway/send",
|
||||||
|
"params_outgoing": "secret=something+secret&to=%(phone_number)s&txt=%(message)s",
|
||||||
|
"params_incoming": "from=%(phone_number)s&txt=%(message)s"
|
||||||
|
|
||||||
|
},
|
||||||
|
"message_tester": {
|
||||||
|
"ENGINE": "rapidsms.backends.bucket",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# to help you get started quickly, many django/rapidsms apps are enabled
|
||||||
|
# by default. you may wish to remove some and/or add your own.
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
|
||||||
|
# the essentials.
|
||||||
|
"django_nose",
|
||||||
|
"djtables",
|
||||||
|
"rapidsms",
|
||||||
|
|
||||||
|
# common dependencies (which don't clutter up the ui).
|
||||||
|
"rapidsms.contrib.handlers",
|
||||||
|
"rapidsms.contrib.ajax",
|
||||||
|
|
||||||
|
# enable the django admin using a little shim app (which includes
|
||||||
|
# the required urlpatterns), and a bunch of undocumented apps that
|
||||||
|
# the AdminSite seems to explode without.
|
||||||
|
"django.contrib.sites",
|
||||||
|
"django.contrib.auth",
|
||||||
|
"django.contrib.admin",
|
||||||
|
"django.contrib.sessions",
|
||||||
|
"django.contrib.contenttypes",
|
||||||
|
|
||||||
|
# the rapidsms contrib apps.
|
||||||
|
# "rapidsms.contrib.default",
|
||||||
|
# "rapidsms.contrib.export",
|
||||||
|
"rapidsms.contrib.httptester",
|
||||||
|
# "rapidsms.contrib.locations",
|
||||||
|
"rapidsms.contrib.messagelog",
|
||||||
|
"rapidsms.contrib.messaging",
|
||||||
|
"rapidsms.contrib.registration",
|
||||||
|
# "rapidsms.contrib.scheduler",
|
||||||
|
# "rapidsms.contrib.echo",
|
||||||
|
|
||||||
|
"mumbai"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# this rapidsms-specific setting defines which views are linked by the
|
||||||
|
# tabbed navigation. when adding an app to INSTALLED_APPS, you may wish
|
||||||
|
# to add it here, also, to expose it in the rapidsms ui.
|
||||||
|
RAPIDSMS_TABS = [
|
||||||
|
("rapidsms.contrib.messagelog.views.message_log", "Message Log"),
|
||||||
|
("rapidsms.contrib.registration.views.registration", "Registration"),
|
||||||
|
("rapidsms.contrib.messaging.views.messaging", "Messaging"),
|
||||||
|
# ("rapidsms.contrib.locations.views.locations", "Map"),
|
||||||
|
# ("rapidsms.contrib.scheduler.views.index", "Event Scheduler"),
|
||||||
|
("rapidsms.contrib.httptester.views.generate_identity", "Message Tester"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------- #
|
||||||
|
# BORING CONFIGURATION #
|
||||||
|
# -------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
# debug mode is turned on as default, since rapidsms is under heavy
|
||||||
|
# development at the moment, and full stack traces are very useful
|
||||||
|
# when reporting bugs. don't forget to turn this off in production.
|
||||||
|
DEBUG = TEMPLATE_DEBUG = True
|
||||||
|
|
||||||
|
|
||||||
|
# after login (which is handled by django.contrib.auth), redirect to the
|
||||||
|
# dashboard rather than 'accounts/profile' (the default).
|
||||||
|
LOGIN_REDIRECT_URL = "/"
|
||||||
|
|
||||||
|
|
||||||
|
# use django-nose to run tests. rapidsms contains lots of packages and
|
||||||
|
# modules which django does not find automatically, and importing them
|
||||||
|
# all manually is tiresome and error-prone.
|
||||||
|
TEST_RUNNER = "django_nose.NoseTestSuiteRunner"
|
||||||
|
|
||||||
|
|
||||||
|
# for some reason this setting is blank in django's global_settings.py,
|
||||||
|
# but it is needed for static assets to be linkable.
|
||||||
|
MEDIA_URL = "/static/"
|
||||||
|
|
||||||
|
|
||||||
|
# this is required for the django.contrib.sites tests to run, but also
|
||||||
|
# not included in global_settings.py, and is almost always ``1``.
|
||||||
|
# see: http://docs.djangoproject.com/en/dev/ref/contrib/sites/
|
||||||
|
SITE_ID = 1
|
||||||
|
|
||||||
|
|
||||||
|
# the default log settings are very noisy.
|
||||||
|
LOG_LEVEL = "DEBUG"
|
||||||
|
LOG_FILE = "rapidsms.log"
|
||||||
|
LOG_FORMAT = "[%(name)s]: %(message)s"
|
||||||
|
LOG_SIZE = 8192 # 8192 bits = 8 kb
|
||||||
|
LOG_BACKUPS = 256 # number of logs to keep
|
||||||
|
|
||||||
|
|
||||||
|
# these weird dependencies should be handled by their respective apps,
|
||||||
|
# but they're not, so here they are. most of them are for django admin.
|
||||||
|
TEMPLATE_CONTEXT_PROCESSORS = [
|
||||||
|
"django.core.context_processors.auth",
|
||||||
|
"django.core.context_processors.debug",
|
||||||
|
"django.core.context_processors.i18n",
|
||||||
|
"django.core.context_processors.media",
|
||||||
|
"django.core.context_processors.request",
|
||||||
|
]
|
||||||
|
|
||||||
|
# template loaders load templates from various places.
|
||||||
|
# for djtables to work properly, the egg loader needs to be
|
||||||
|
# included, the others are fairly standard.
|
||||||
|
TEMPLATE_LOADERS = (
|
||||||
|
'django.template.loaders.filesystem.Loader',
|
||||||
|
'django.template.loaders.app_directories.Loader',
|
||||||
|
'django.template.loaders.eggs.Loader'
|
||||||
|
)
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------- #
|
||||||
|
# HERE BE DRAGONS! #
|
||||||
|
# these settings are pure hackery, and will go away soon #
|
||||||
|
# -------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
# these apps should not be started by rapidsms in your tests, however,
|
||||||
|
# the models and bootstrap will still be available through django.
|
||||||
|
TEST_EXCLUDED_APPS = [
|
||||||
|
"django.contrib.sessions",
|
||||||
|
"django.contrib.contenttypes",
|
||||||
|
"django.contrib.auth",
|
||||||
|
"rapidsms",
|
||||||
|
"rapidsms.contrib.ajax",
|
||||||
|
"rapidsms.contrib.httptester",
|
||||||
|
]
|
||||||
|
|
||||||
|
# the project-level url patterns
|
||||||
|
ROOT_URLCONF = "urls"
|
||||||
|
|
||||||
|
# import local_settings.py
|
||||||
|
try:
|
||||||
|
from local_settings import *
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# since we might hit the database from any thread during testing, the
|
||||||
|
# in-memory sqlite database isn't sufficient. it spawns a separate
|
||||||
|
# virtual database for each thread, and syncdb is only called for the
|
||||||
|
# first. this leads to confusing "no such table" errors. We create
|
||||||
|
# a named temporary instance instead.
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if 'test' in sys.argv:
|
||||||
|
for db_name in DATABASES:
|
||||||
|
DATABASES[db_name]['TEST_NAME'] = os.path.join(
|
||||||
|
tempfile.gettempdir(),
|
||||||
|
"%s.rapidsms.test.sqlite3" % db_name)
|
||||||
|
|
37
smsBEST/urls.py
Normal file
37
smsBEST/urls.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
from django.conf.urls.defaults import *
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
admin.autodiscover()
|
||||||
|
|
||||||
|
urlpatterns = patterns('',
|
||||||
|
# Example:
|
||||||
|
# (r'^my-project/', include('my_project.foo.urls')),
|
||||||
|
|
||||||
|
# Uncomment the admin/doc line below to enable admin documentation:
|
||||||
|
# (r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||||
|
|
||||||
|
(r'^admin/', include(admin.site.urls)),
|
||||||
|
|
||||||
|
# RapidSMS core URLs
|
||||||
|
(r'^account/', include('rapidsms.urls.login_logout')),
|
||||||
|
url(r'^$', 'rapidsms.views.dashboard', name='rapidsms-dashboard'),
|
||||||
|
|
||||||
|
# RapidSMS contrib app URLs
|
||||||
|
(r'^ajax/', include('rapidsms.contrib.ajax.urls')),
|
||||||
|
(r'^export/', include('rapidsms.contrib.export.urls')),
|
||||||
|
(r'^httptester/', include('rapidsms.contrib.httptester.urls')),
|
||||||
|
(r'^locations/', include('rapidsms.contrib.locations.urls')),
|
||||||
|
(r'^messagelog/', include('rapidsms.contrib.messagelog.urls')),
|
||||||
|
(r'^messaging/', include('rapidsms.contrib.messaging.urls')),
|
||||||
|
(r'^registration/', include('rapidsms.contrib.registration.urls')),
|
||||||
|
(r'^scheduler/', include('rapidsms.contrib.scheduler.urls')),
|
||||||
|
)
|
||||||
|
|
||||||
|
if settings.DEBUG:
|
||||||
|
urlpatterns += patterns('',
|
||||||
|
# helper URLs file that automatically serves the 'static' folder in
|
||||||
|
# INSTALLED_APPS via the Django static media server (NOT for use in
|
||||||
|
# production)
|
||||||
|
(r'^', include('rapidsms.urls.static_media')),
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user