Merge lp:~the-dod/sahana-eden/twitter-oauth into lp:sahana-eden

Proposed by The Dod
Status: Superseded
Proposed branch: lp:~the-dod/sahana-eden/twitter-oauth
Merge into: lp:sahana-eden
Diff against target: 340 lines (+157/-33)
7 files modified
controllers/msg.py (+78/-16)
cron/crontab (+2/-2)
models/000_config.py (+21/-13)
models/00_utils.py (+1/-1)
models/01_menu.py (+1/-0)
models/msg.py (+47/-1)
modules/s3cfg.py (+7/-0)
To merge this branch: bzr merge lp:~the-dod/sahana-eden/twitter-oauth
Reviewer Review Type Date Requested Status
Fran Boon Pending
Review via email: mp+38119@code.launchpad.net

This proposal has been superseded by a proposal from 2010-10-11.

Description of the change

Twitter-OAuth integration, phase 1.
See comment on the commit.
Hope I'm doing everything right being new to s3 and all :)

To post a comment you must log in.
1348. By Fran Boon

Merge thedod: Twitter OAuth support using tweepy. Also did general cleanup of Messaging

1349. By The Dod <nonboong@nobita>

Merge with Fran et al

1350. By The Dod <nonboong@nobita>

Proposal to separate deployment-specific files from version-control

In order to prevent leaks of deployment-specific information into the
repoository, I propose to track *templates* of these files instead.
After installation, admin should run ./run_me_once.sh
to create *untracked* versions of these files where passwords etc.
can be safely entered.

1351. By The Dod <nonboong@nobita>

Merge with trunk

1352. By The Dod <nonboong@nobita>

revert to before skeletons proposal

1353. By The Dod <nonboong@nobita>

Wrote the whole twitter sending bit

Sadly - however - it doesn't work.
Got text waiting to be tweeted in the outbox, and the cron doesn't seem
to pick it up. Committing this so that I can bitch about it in irc :)

Unmerged revisions

1353. By The Dod <nonboong@nobita>

Wrote the whole twitter sending bit

Sadly - however - it doesn't work.
Got text waiting to be tweeted in the outbox, and the cron doesn't seem
to pick it up. Committing this so that I can bitch about it in irc :)

1352. By The Dod <nonboong@nobita>

revert to before skeletons proposal

1351. By The Dod <nonboong@nobita>

Merge with trunk

1350. By The Dod <nonboong@nobita>

Proposal to separate deployment-specific files from version-control

In order to prevent leaks of deployment-specific information into the
repoository, I propose to track *templates* of these files instead.
After installation, admin should run ./run_me_once.sh
to create *untracked* versions of these files where passwords etc.
can be safely entered.

1349. By The Dod <nonboong@nobita>

Merge with Fran et al

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'controllers/msg.py'
2--- controllers/msg.py 2010-10-10 05:06:31 +0000
3+++ controllers/msg.py 2010-10-11 13:53:44 +0000
4@@ -12,13 +12,13 @@
5
6 # Options Menu (available in all Functions' Views)
7 response.menu_options = [
8- [T("Compose"), False, URL(r=request, c="msg", f="compose")],
9- [T("Distribution groups"), False, URL(r=request, f="group"), [
10- [T("List/Add"), False, URL(r=request, f="group")],
11- [T("Group Memberships"), False, URL(r=request, f="group_membership")],
12- ]],
13- [T("Log"), False, URL(r=request, f="log")],
14- [T("Outbox"), False, URL(r=request, f="outbox")],
15+ [T("Compose"), False, URL(r=request, c="msg", f="compose")],
16+ [T("Distribution groups"), False, URL(r=request, f="group"), [
17+ [T("List/Add"), False, URL(r=request, f="group")],
18+ [T("Group Memberships"), False, URL(r=request, f="group_membership")],
19+ ]],
20+ [T("Log"), False, URL(r=request, f="log")],
21+ [T("Outbox"), False, URL(r=request, f="outbox")],
22 #["CAP", False, URL(r=request, f="tbc")]
23 ]
24
25@@ -228,8 +228,8 @@
26 _title=T("Delete") + "|" + T("If this is set to True then mails will be deleted from the server after downloading.")))
27
28 if not auth.has_membership(auth.id_group("Administrator")):
29- session.error = UNAUTHORISED
30- redirect(URL(r=request, f="index"))
31+ session.error = UNAUTHORISED
32+ redirect(URL(r=request, f="index"))
33 # CRUD Strings
34 ADD_SETTING = T("Add Setting")
35 VIEW_SETTINGS = T("View Settings")
36@@ -251,6 +251,69 @@
37 response.menu_options = admin_menu_options
38 return shn_rest_controller(module, "email_settings", listadd=False, deletable=False)
39
40+@auth.shn_requires_membership(1)
41+def twitter_settings():
42+ """ RESTful CRUD controller for twitter settings - appears in the administration menu """
43+
44+ # CRUD Strings
45+ ADD_SETTING = T("Add Setting")
46+ VIEW_SETTINGS = T("View Settings")
47+ s3.crud_strings[tablename] = Storage(
48+ title_create = ADD_SETTING,
49+ title_display = T("Setting Details"),
50+ title_list = VIEW_SETTINGS,
51+ title_update = T("Authenticate system's Twitter account"),
52+ title_search = T("Search Settings"),
53+ subtitle_list = T("Settings"),
54+ label_list_button = VIEW_SETTINGS,
55+ label_create_button = ADD_SETTING,
56+ msg_record_created = T("Setting added"),
57+ msg_record_modified = T("System's Twitter account updated"),
58+ msg_record_deleted = T("Setting deleted"),
59+ msg_list_empty = T("No Settings currently defined")
60+ )
61+
62+ def prep(jr):
63+ if not (deployment_settings.oauth.consumer_key and deployment_settings.oauth.consumer_secret):
64+ session.error=T("You should edit oauth_settings at models/000_config.py")
65+ return True
66+ try:
67+ import tweepy
68+ except:
69+ session.error=T("Couldn't import tweepy library")
70+ return True
71+ oauth = tweepy.OAuthHandler(deployment_settings.oauth.consumer_key,
72+ deployment_settings.oauth.consumer_secret)
73+
74+ resource = request.function
75+ tablename = module + "_" + resource
76+ table = db[tablename]
77+
78+ if jr.http == "GET":
79+ try:
80+ session.s3.twitter_oauth_url = oauth.get_authorization_url()
81+ session.s3.twitter_request_key = oauth.request_token.key
82+ session.s3.twitter_request_secret = oauth.request_token.secret
83+ except tweepy.TweepError:
84+ session.error=T("problem connecting to twitter.com")
85+ return True
86+ table.pin.label = SPAN(T("PIN number "),
87+ A(T("from Twitter"), _href=T(session.s3.twitter_oauth_url), _target="_blank"),
88+ T(" (leave empty to detach account)"))
89+ table.pin.value = ""
90+ table.twitter_account.label = T("Current twitter account")
91+ else: # POST
92+ table.pin.label = T("PIN")
93+ table.pin.value = ""
94+ table.oauth_key.label = T("OAuth key")
95+ table.oauth_secret.label = T("OAuth secret")
96+ return True
97+ response.s3.prep = prep
98+
99+ response.menu_options = admin_menu_options
100+ return shn_rest_controller(module, "twitter_settings", listadd=False, deletable=False)
101+
102+
103 #--------------------------------------------------------------------------------------------------
104
105 # The following 2 functions hook into the pr functions
106@@ -395,16 +458,16 @@
107 return item
108
109 def process_sms_via_api():
110- "Controller for SMS api processing - to be called via cron"
111+ "Controller for SMS api processing - to be called via cron"
112
113- msg.process_outbox(contact_method = 2)
114- return
115+ msg.process_outbox(contact_method = 2)
116+ return
117
118 def process_email_via_api():
119- "Controller for Email api processing - to be called via cron"
120+ "Controller for Email api processing - to be called via cron"
121
122- msg.process_outbox(contact_method = 1)
123- return
124+ msg.process_outbox(contact_method = 1)
125+ return
126
127 def process_sms_via_tropo():
128 "Controller for SMS tropo processing - to be called via cron"
129@@ -632,4 +695,3 @@
130 response.s3.pagination = True
131
132 return shn_rest_controller(module, resource, listadd=False)
133-
134
135=== modified file 'cron/crontab'
136--- cron/crontab 2010-10-10 20:04:29 +0000
137+++ cron/crontab 2010-10-11 13:53:44 +0000
138@@ -5,9 +5,9 @@
139 #@reboot root *applications/eden/cron/sms_handler_modem.py
140 # Synchronisation
141 #@reboot root *sync/schedule_cron
142-0 * * * * root *sync/sync_cron
143+0 * * * * web2py *sync/sync_cron
144 # Send outgoing emails every 5 minutes
145-#*/5 * * * * root *msg/process_email_via_api
146+*/5 * * * * web2py *msg/process_email_via_api
147 # Send outgoing SMS every 5 minutes
148 #*/5 * * * * root *msg/process_sms_via_api
149 # Send outgoing Tropo SMS every 5 minutes
150
151=== modified file 'models/000_config.py'
152--- models/000_config.py 2010-10-10 10:16:05 +0000
153+++ models/000_config.py 2010-10-11 13:53:44 +0000
154@@ -27,6 +27,14 @@
155 deployment_settings.auth.registration_requires_approval = False
156 deployment_settings.auth.openid = False
157
158+# Twitter OAuth settings:
159+# Register an app at http://twitter.com/apps
160+# (select Aplication Type: Client)
161+# You'll get your consumer_key and consumer_secret from twitter
162+# Keep these empty if you don't need twitter integration
163+deployment_settings.oauth.consumer_key=""
164+deployment_settings.oauth.consumer_secret=""
165+
166 # Base settings
167 # Set this to the Public URL of the instance
168 deployment_settings.base.public_url = "http://127.0.0.1:8000"
169@@ -57,14 +65,14 @@
170
171 # Email settings
172 # Outbound server
173-deployment_settings.mail.server = "127.0.0.1:25"
174+#deployment_settings.mail.server = "127.0.0.1:25"
175 # Useful for Windows Laptops:
176-#deployment_settings.mail.server = "smtp.gmail.com:587"
177-#deployment_settings.mail.login = "username:password"
178+deployment_settings.mail.server = "smtp.gmail.com:587"
179+deployment_settings.mail.login = "gravitatz@gmail.com:"+"rganiyh,qnevz".encode('rot13') # protection from *honest* folks :)
180 # From Address
181-deployment_settings.mail.sender = "'Sahana' <sahana@your.org>"
182+deployment_settings.mail.sender = "'Sahana bot' <gravitatz@gmail.com>"
183 # Address to which mails get sent to approve new users
184-deployment_settings.mail.approver = "useradmin@your.org"
185+deployment_settings.mail.approver = "gravitatz@gmail.com"
186
187 # L10n settings
188 # Uncomment this if the deployment is just in a few countries
189@@ -254,7 +262,7 @@
190 pr_address = {"importer" : True},
191 pr_pe_contact = {"importer" : True},
192 pr_presence = {"importer" : True},
193- pr_identity = {"importer" : True},
194+ pr_identity = {"importer" : True},
195 pr_person = {"importer" : True},
196 pr_group = {"importer" : True},
197 pr_group_membership = {"importer" : True},
198@@ -327,18 +335,18 @@
199 # module_type = 10,
200 # ),
201 importer = Storage(
202- name_nice = "Spreadsheet Importer",
203- description = "Used to import data from spreadsheets into the database",
204- module_type = 10,
205+ name_nice = "Spreadsheet Importer",
206+ description = "Used to import data from spreadsheets into the database",
207+ module_type = 10,
208 ),
209 survey = Storage(
210- name_nice = "Survey Module",
211- description = "Create, enter, and manage surveys.",
212- module_type = 10,
213+ name_nice = "Survey Module",
214+ description = "Create, enter, and manage surveys.",
215+ module_type = 10,
216 )
217 #lms = Storage(
218 # name_nice = T("Logistics Management System"),
219 # description = T("An intake system, a warehouse management system, commodity tracking, supply chain management, procurement and other asset and resource management capabilities."),
220 # module_type = 10
221 # ),
222-)
223\ No newline at end of file
224+)
225
226=== modified file 'models/00_utils.py'
227--- models/00_utils.py 2010-10-04 21:57:29 +0000
228+++ models/00_utils.py 2010-10-11 13:53:44 +0000
229@@ -38,7 +38,7 @@
230 # Security Policy
231 #session.s3.self_registration = deployment_settings.get_security_self_registration()
232 session.s3.security_policy = deployment_settings.get_security_policy()
233-
234+
235 # We Audit if either the Global or Module asks us to
236 # (ignore gracefully if module author hasn't implemented this)
237 try:
238
239=== modified file 'models/01_menu.py'
240--- models/01_menu.py 2010-10-09 20:35:42 +0000
241+++ models/01_menu.py 2010-10-11 13:53:44 +0000
242@@ -91,6 +91,7 @@
243 [T("Messaging"), False, "#",[
244 [T("Global Messaging Settings"), False, URL(r=request, c="msg", f="setting", args=[1, "update"])],
245 [T("Email Settings"), False, URL(r=request, c="msg", f="email_settings", args=[1, "update"])],
246+ [T("Twitter Settings"), False, URL(r=request, c="msg", f="twitter_settings", args=[1, "update"])],
247 [T("Modem Settings"), False, URL(r=request, c="msg", f="modem_settings", args=[1, "update"])],
248 [T("Gateway Settings"), False, URL(r=request, c="msg", f="gateway_settings", args=[1, "update"])],
249 [T("Tropo Settings"), False, URL(r=request, c="msg", f="tropo_settings", args=[1, "update"])],
250
251=== modified file 'models/msg.py'
252--- models/msg.py 2010-10-10 20:14:10 +0000
253+++ models/msg.py 2010-10-11 13:53:44 +0000
254@@ -6,7 +6,6 @@
255
256 module = "msg"
257 if deployment_settings.has_module(module):
258-
259 # Settings
260 resource = "setting"
261 tablename = "%s_%s" % (module, resource)
262@@ -18,6 +17,53 @@
263 table.outgoing_sms_handler.requires = IS_IN_SET(["Modem","Gateway","Tropo"], zero=None)
264
265 #------------------------------------------------------------------------
266+ resource="twitter_settings"
267+ tablename = "%s_%s" % (module, resource)
268+ table = db.define_table(tablename,
269+ Field("pin"),
270+ Field("oauth_key"),
271+ Field("oauth_secret"),
272+ Field("twitter_account"),
273+ migrate=migrate)
274+ table.oauth_key.writable = False
275+ table.oauth_secret.writable = False
276+
277+ ### comment these 2 when debugging
278+ table.oauth_key.readable = False
279+ table.oauth_secret.readable = False
280+
281+ table.twitter_account.writable = False
282+
283+ def twitter_settings_onvalidation(form):
284+ """ Complete oauth: take tokens from session + pin from form, and do the 2nd API call to twitter """
285+ if form.vars.pin and session.s3.twitter_request_key and session.s3.twitter_request_secret:
286+ try:
287+ import tweepy
288+ except:
289+ raise HTTP(501,body="can't import tweepy")
290+
291+ oauth = tweepy.OAuthHandler(deployment_settings.oauth.consumer_key,
292+ deployment_settings.oauth.consumer_secret)
293+ oauth.set_request_token(session.s3.twitter_request_key,session.s3.twitter_request_secret)
294+ try:
295+ oauth.get_access_token(form.vars.pin)
296+ form.vars.oauth_key = oauth.access_token.key
297+ form.vars.oauth_secret = oauth.access_token.secret
298+ twitter = tweepy.API(oauth)
299+ form.vars.twitter_account = twitter.me().screen_name
300+ form.vars.pin = "" # we won't need it anymore
301+ return
302+ except tweepy.TweepError:
303+ session.error=T("Settings were reset because authenticating with twitter failed")
304+ # Either user asked to reset, or error - clear everything
305+ for k in ['oauth_key','oauth_secret','twitter_account']:
306+ form.vars[k] = ""
307+ for k in ['twitter_request_key','twitter_request_secret']:
308+ session.s3[k] = ""
309+
310+ s3xrc.model.configure(table, onvalidation=twitter_settings_onvalidation)
311+
312+ #------------------------------------------------------------------------
313 resource = "email_settings"
314 tablename = "%s_%s" % (module, resource)
315 table = db.define_table(tablename,
316
317=== modified file 'modules/s3cfg.py'
318--- modules/s3cfg.py 2010-09-21 06:34:46 +0000
319+++ modules/s3cfg.py 2010-10-11 13:53:44 +0000
320@@ -11,6 +11,7 @@
321 self.database = Storage()
322 self.gis = Storage()
323 self.mail = Storage()
324+ self.oauth = Storage()
325 self.L10n = Storage()
326 self.security = Storage()
327 self.ui = Storage()
328@@ -107,6 +108,12 @@
329 def get_gis_spatialdb(self):
330 return self.gis.get("spatialdb", False)
331
332+ # OAuth settings
333+ def get_oauth_consumer_key(self):
334+ return self.oauth.get("consumer_key","")
335+ def get_oauth_consumer_secret(self):
336+ return self.oauth.get("consumer_secret","")
337+
338 # L10N Settings
339 def get_L10n_countries(self):
340 return self.L10n.get("countries", "")