source: trunk/call/call.py@ 494

Last change on this file since 494 was 461, checked in by Nicholas Riley, 16 years ago

Use string continuations to improve readability.

File size: 5.3 KB
Line 
1from google.appengine.api import urlfetch, users
2from google.appengine.ext import db
3from google.appengine.ext.webapp import RequestHandler, WSGIApplication, template
4from google.appengine.ext.db import djangoforms
5from urllib import urlencode
6from wsgiref.handlers import CGIHandler
7
8try:
9 from django import newforms as forms
10except ImportError:
11 from django import forms
12
13try:
14 from django.contrib.localflavor.us.forms import USPhoneNumberField
15except ImportError:
16 from django.contrib.localflavor.usa.forms import USPhoneNumberField
17
18import logging, os, re
19
20logging.getLogger().setLevel(logging.DEBUG)
21
22CLICK2CALL_URL = 'https://secure.click2callu.com/tpcc/makecall'
23
24def vonageize_number(number):
25 number = re.sub(r'\D', '', number)
26 if len(number) == 0:
27 raise forms.ValidationError('Please enter a telephone number.')
28 if number[0] == '1':
29 number = number[1:]
30 if len(number) != 10:
31 raise forms.ValidationError('Please enter a 10-digit US telephone '
32 'number.')
33 return '1' + number
34
35class VonageUSNumberProperty(db.PhoneNumberProperty):
36 def validate(self, value):
37 return vonageize_number(value)
38
39class VonageNumber(db.Model):
40 label = db.StringProperty()
41 username = db.StringProperty(required=True)
42 password = db.StringProperty(required=True)
43 number = VonageUSNumberProperty(required=True)
44
45class AllowedUser(db.Model):
46 user = db.UserProperty(required=True)
47
48def login_required(f):
49 def handle(self):
50 user = users.get_current_user()
51 if user is None:
52 return self.redirect(users.create_login_url(self.request.uri))
53 if AllowedUser.gql('WHERE user = :1', user).get() is None:
54 if users.is_current_user_admin():
55 AllowedUser(user=user).put()
56 else:
57 email = user.email()
58 logging.error('Denied access to ' + email)
59 return self.unavailable(
60 'Your account <b>%s</b> does not have permission to use '
61 'this service.' % email)
62 return f(self)
63 return handle
64
65def admin_required(f):
66 def handle(self):
67 if not users.is_current_user_admin():
68 return self.unavailable(
69 'This function requires you to be an administrator.')
70 return f(self)
71 return handle
72
73class NumberForm(djangoforms.ModelForm):
74 password = forms.CharField(widget=forms.widgets.PasswordInput)
75 number = USPhoneNumberField(label='Vonage number')
76
77 class Meta:
78 model = VonageNumber
79 exclude = ['label']
80
81class BasePage(RequestHandler):
82 def render(self, **kw):
83 self.response.out.write(template.render(self.template_path, kw))
84
85 def unavailable(self, msg):
86 self.render(unavailable=msg,
87 logout_url=users.create_logout_url(self.request.uri))
88
89class NumbersPage(BasePage):
90 template_path = os.path.join(os.path.dirname(__file__), 'numbers.html')
91
92 @admin_required
93 def get(self):
94 self.render(form=NumberForm(instance=VonageNumber.all().get()))
95
96 @admin_required
97 def post(self):
98 form = NumberForm(data=self.request.POST,
99 instance=VonageNumber.all().get())
100 if not form.is_valid():
101 return self.render(form=form)
102 try:
103 form.save()
104 except Exception, e:
105 return self.render(form=form, error=e.message)
106 self.redirect('/')
107
108class MainPage(BasePage):
109 template_path = os.path.join(os.path.dirname(__file__), 'index.html')
110
111 def error(self, msg, **kw):
112 self.render(error=msg, tonumber=self.request.get('tonumber', ''), **kw)
113
114 def failure(self, msg):
115 logging.error('TPCC server error: ' + msg)
116 self.error('The Vonage Click2Call server could not complete your call.',
117 failure=msg)
118
119 def no_number(self):
120 if users.is_current_user_admin():
121 return self.redirect('/numbers')
122 self.unavailable('No phone numbers have been configured. Please ask an '
123 'administrator to log in and add a phone number.')
124
125 @login_required
126 def get(self):
127 if VonageNumber.all().get() is None:
128 return self.no_number()
129 self.render()
130
131 @login_required
132 def post(self):
133 fromnumber = VonageNumber.all().get()
134 if fromnumber is None:
135 return self.no_number()
136
137 try:
138 tonumber = vonageize_number(self.request.get('tonumber', ''))
139 except Exception, e:
140 return self.error(e.message)
141
142 body = urlencode(
143 dict(username=fromnumber.username, password=fromnumber.password,
144 fromnumber=fromnumber.number, tonumber=tonumber))
145 try:
146 response = urlfetch.fetch(CLICK2CALL_URL + '?' + body)
147 # get random auth errors with body, urlfetch.POST)
148 except urlfetch.Error, e:
149 return self.failure(e.message)
150
151 if response.status_code != 200 or response.content[:3] != '000':
152 return self.failure(response.content)
153
154 logging.info('Dialing %s' % tonumber)
155 return self.render(
156 message='Dialing. Please answer your phone when it rings.')
157
158def main():
159 CGIHandler().run(WSGIApplication([('/', MainPage),
160 ('/numbers', NumbersPage)], debug=True))
161
162if __name__ == "__main__":
163 main()
Note: See TracBrowser for help on using the repository browser.