From: mako@atdot.cc <> Date: Tue, 2 Oct 2007 13:31:28 +0000 (-0400) Subject: initial version of the sms written at DBA X-Git-Url: https://projects.mako.cc/source/rapidsms/commitdiff_plain/1e678b8dfe9cc8473d0e4b14f554db44312ef8b4 initial version of the sms written at DBA --- 1e678b8dfe9cc8473d0e4b14f554db44312ef8b4 diff --git a/create.sql b/create.sql new file mode 100644 index 0000000..49705a7 --- /dev/null +++ b/create.sql @@ -0,0 +1,38 @@ +DROP TABLE IF EXISTS people; +CREATE TABLE people ( + id INT NOT NULL auto_increment, + name VARCHAR(255) NULL DEFAULT NULL, + phone_number VARCHAR(255) NOT NULL, + PRIMARY KEY (id) +); + +DROP TABLE IF EXISTS questionaires; +CREATE TABLE questionaires ( + id INT NOT NULL auto_increment, + code VARCHAR(100) NOT NULL, -- always a letter + description VARCHAR(255) NOT NULL, + PRIMARY KEY (id) +); + +DROP TABLE IF EXISTS questions; +CREATE TABLE questions ( + id INT NOT NULL auto_increment, + questionaire_id INT NOT NULL, + code VARCHAR(100) NOT NULL , -- always a number + longdesc VARCHAR(255) NULL DEFAULT NULL, + shortdesc VARCHAR(20) NULL DEFAULT NULL, + datatype VARCHAR(100) NOT NULL, -- number, char, bool + PRIMARY KEY (id) +); + +DROP TABLE IF EXISTS answers; +CREATE TABLE answers ( + id INT NOT NULL auto_increment, + question_id INT NOT NULL, + person_id INT NOT NULL, + answer VARCHAR(255) NOT NULL, + recieved TIMESTAMP NOT NULL DEFAULT NOW(), + PRIMARY KEY (id) +); + +-- add group table in the future diff --git a/db.py b/db.py new file mode 100644 index 0000000..c0e0640 --- /dev/null +++ b/db.py @@ -0,0 +1,60 @@ +from storm.locals import * +database = create_database("mysql:uniphone") +store = Store(database) + +class Person(object): + __storm_table__ = "people" + id = Int(primary=True) + name = Unicode() + phone_number = Unicode() + + def __init__(self, **kw): + self.name = unicode(kw['name']) + self.phone_number = unicode(kw['phone_number']) + +class Questionaire(object): + __storm_table__ = "questionaires" + id = Int(primary=True) + code = Unicode() + description = Unicode() + + def __init__(self, **kw): + self.code = unicode(kw['code']) + self.description = unicode(kw['description']) + +class Question(object): + __storm_table__ = "questions" + id = Int(primary=True) + code = Unicode() + longdesc = Unicode() + shortdesc = Unicode() + datatype = Unicode() + questionaire_id = Int() + questionaire = Reference(questionaire_id, Questionaire.id) + + def __init__(self, **kw): + self.code = unicode(kw['code']) + self.longdesc = unicode(kw['longdesc']) + self.shortdesc = unicode(kw['shortdesc']) + self.datatype = unicode(kw['datatype']) + self.questionaire_id = int(kw['questionaire_id']) + +class Answer(object): + __storm_table__ = "answers" + id = Int(primary=True) + answer = Unicode() + received = DateTime() + question_id = Int() + question = Reference(question_id, Question.id) + people_id = Int() + person = Reference(people_id, Person.id) + + def __init__(self, **kw): + self.answer = unicode(kw['answer']) + self.received = unicode(kw['received']) # have this change to the right type + +# define the many to one relationships +Person.answers = ReferenceSet(Person.id, Answer.id) +Questionaire.questions = ReferenceSet(Questionaire.id, Question.questionaire_id) +Question.answers = ReferenceSet(Question.id, Answer.id) + diff --git a/rapidsms.py b/rapidsms.py new file mode 100755 index 0000000..92a441b --- /dev/null +++ b/rapidsms.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python + +# UNICEF SMS Phone Application +# +# Copyright (C) 2007 Benjamin Mako Hill +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the Affero General Public License as published +# by the Free Software Foundation, either version 1 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the Affero General Public License +# along with this program. If not, see +# . + +import web +import sys, os, re + +from db import * + +# recalculate the path based on the location of the file +from jinja import Environment, FileSystemLoader +jinja_env = Environment(loader=FileSystemLoader(os.path.dirname(__file__) + "/templates/")) + +def render(filename, vars={}): + """Render Jinja Templates""" + + web.header("Content-Type","text/html; charset=utf-8") + + tmpl = jinja_env.get_template(filename) + + vars['ctx'] = web.ctx + vars['home'] = web.ctx.home + vars['homepath'] = web.ctx.homepath + #vars['session'] = web.ctx.environ['com.saddi.service'].session + + print tmpl.render(vars) + +# the url map for the application +urls = ( '/?', 'index', + '/?new_name', 'new_name', + '/?show_names', 'show_names', + '/?questionaires', 'questionaires', + '/?edit_questions/(\d+)', 'edit_questions') + +class index: + def GET(self): + render('index.tmpl') + +class new_name: + def GET(self): + render('new_name.tmpl') + + def POST(self): + input = web.input() + + errors = [] + if not input.name: + errors.append('You must provide a name.') + if not input.phone_number: + errors.append('You must provide a phone number.') + elif not re.match(r'[0-9\+\- ]', input.phone_number): + errors.append('Your phone number must be only numbers.') + + if not errors: + for error in self.validate_phone(input.phone_number): + errors.append(error) + if errors: + render('new_name.tmpl', locals()) + else: + # try to save it to the database + new_person = Person(name=input.name, phone_number=input.phone_number) + # TODO make this work + # errors.append('There was an error saving to the database') + store.add(new_person) + store.commit() + render('success.tmpl', locals()) + + def validate_phone(self, phone_number): + errors = [] + if len(phone_number) < 7: + errors.append('Your phone number is too short') + else: + people = store.find(Person) + for person in people: + if phone_number[-7:] == person.phone_number[-7:]: + errors.append('That phone number already exists in our datbase.') + break + + return(errors) + +class show_names: + def GET(self): + people = store.find(Person) + render('show_names.tmpl', locals()) + + +class questionaires: + def GET(self): + questionaires = store.find(Questionaire) + render('questionaires.tmpl', locals()) + + def POST(self): + questionaires = store.find(Questionaire) + input = web.input() + + errors = [] + if not input.code: + errors.append('You must enter a code.') + + if not errors: + new_questionaire = Questionaire(code=input.code, + description=input.description) + store.add(new_questionaire) + store.commit() + + render('questionaires.tmpl', locals()) + +class edit_questions: + def GET(self, id): + input = web.input() + questionaire = store.get(Questionaire, int(id)) + render('edit_questions.tmpl', locals()) + + def POST(self, id): + input = web.input() + questionaire = store.get(Questionaire, int(id)) + + errors = [] + if not input.longdesc: + errors.append('You must have a long description.') + + code = self.__get_code(questionaire) + + if not errors: + new_question = Question(code=code, + shortdesc=input.shortdesc, + datatype=input.datatype, + longdesc=input.longdesc, + questionaire_id=questionaire.id) + store.add(new_question) + store.commit() + + render('edit_questions.tmpl', locals()) + + def __get_code(self, questionaire): + """Return a new code that is higher than the others.""" + + codes = list(int(q.code) for q in questionaire.questions) + return(len(codes) + 1) + +class test: + def GET(self): + render('test.tmpl') + + def POST(self): + render('test_sent.tmpl') + +class raw_sms_in: + def POST(self): + #TODO magic to parse sms + pass + + +web.webapi.internalerror = web.debugerror +if __name__ == "__main__": + web.run(urls, globals(), web.reloader) + +application = web.wsgifunc(web.webpyfunc(urls, globals())) + diff --git a/templates/_error.tmpl b/templates/_error.tmpl new file mode 100644 index 0000000..2c07afe --- /dev/null +++ b/templates/_error.tmpl @@ -0,0 +1,9 @@ +
+

Error

+ +
    +{% for error in errors %} +
  • {{error}}
  • +{% endfor %} +
+
diff --git a/templates/edit_questions.tmpl b/templates/edit_questions.tmpl new file mode 100644 index 0000000..9f52033 --- /dev/null +++ b/templates/edit_questions.tmpl @@ -0,0 +1,46 @@ +{% extends 'layout.tmpl' %} + +{% block content %} +

List of Questions for {{questionaire.description}}

+ + + + + + + + + + +{% for q in questionaire.questions %} + + + + + + +{% endfor %} +
CodeLong DescriptionShort DescriptionData Type
{{q.code}}{{q.longdesc}}{{q.shortdesc}}{{q.datatype}}
+ +

New Question

+ +
+ +

+

+ +

+

+ +

+ + +
+ +{% endblock %} diff --git a/templates/index.tmpl b/templates/index.tmpl new file mode 100644 index 0000000..98a1e15 --- /dev/null +++ b/templates/index.tmpl @@ -0,0 +1,10 @@ +{% extends 'layout.tmpl' %} + +{% block content %} + + +{% endblock %} diff --git a/templates/layout.tmpl b/templates/layout.tmpl new file mode 100644 index 0000000..cc48dbe --- /dev/null +++ b/templates/layout.tmpl @@ -0,0 +1,12 @@ + + +UNICEF Phone Application + + +

UNICEF Phone Application

+
+{% block content %} +{% endblock %} +
+ + diff --git a/templates/new_name.tmpl b/templates/new_name.tmpl new file mode 100644 index 0000000..51546d0 --- /dev/null +++ b/templates/new_name.tmpl @@ -0,0 +1,21 @@ +{% extends 'layout.tmpl' %} + +{% block content %} +

Enter Name/Phone Number

+ +{% if errors %} +{% include '_error.tmpl' %} +{% endif %} + +
+ +

+

+ +

+

+ + +
+{% endblock %} diff --git a/templates/questionaires.tmpl b/templates/questionaires.tmpl new file mode 100644 index 0000000..ba8ae24 --- /dev/null +++ b/templates/questionaires.tmpl @@ -0,0 +1,36 @@ +{% extends 'layout.tmpl' %} + +{% block content %} +

List of Questionaires

+ + + + + + + + + +{% for q in questionaires %} + + + + + + +{% endfor %} +
CodeDescriptionNum. Questions
{{q.code}}{{q.description}}{{len(q.questions) or 0}}edit
+ +

New Questionaire

+ +
+

+

+ +

+

+ + +
+ +{% endblock %} diff --git a/templates/show_names.tmpl b/templates/show_names.tmpl new file mode 100644 index 0000000..3308a52 --- /dev/null +++ b/templates/show_names.tmpl @@ -0,0 +1,20 @@ +{% extends 'layout.tmpl' %} + +{% block content %} +

List of Registered Phone Numbers

+ + + + + + + +{% for person in people %} + + + + +{% endfor %} +
NamePhone Number
{{person.name}}{{person.phone_number}}
+ +{% endblock %} diff --git a/templates/success.tmpl b/templates/success.tmpl new file mode 100644 index 0000000..46acb75 --- /dev/null +++ b/templates/success.tmpl @@ -0,0 +1,9 @@ +{% extends 'layout.tmpl' %} + +{% block content %} +

Success!

+ +Successfully entered name {{input.name}} for phone +number {{input.phone_number}}. + +{% endblock %}