1 # -*- coding: utf-8 -*-
5 from twython
import Twython
7 from ConfigParser
import SafeConfigParser
9 from jinja2
import Environment
, FileSystemLoader
13 """We use the Twython module to issue a GET statuses/user_timeline
14 request. The tweets of the response are returned by the method
15 read_user_timeline as list of Unicode strings.
17 These are some API 1.1 docs about the GET statuses/user_timeline requests:
18 https://dev.twitter.com/docs/api/1.1/get/statuses/user_timeline
20 In our tests we found that OAUTH authentication is a prerequisite for GET
21 statuses/timeline requests. The Twython docs are vague about this, cf the
22 comments of the get_user_timeline.py example: "We won't authenticate for
23 this, but sometimes it's necessary"
24 https://raw.github.com/ryanmcgrath/twython/master/core_examples/get_user_timeline.py
26 This class expects three arguments:
27 1. The location of a configuration file with the necessary data for
28 authentication. We use ConfigParser to read the configuration file. See
29 config.sample.ini file.
30 2. A twitter screen_name: "The screen name of the user for whom to return
32 3. A GET statuses/user_timeline count parameter: "Specifies the number of
33 tweets to try and retrieve [...]".
35 At the moment, the HTML jinja2 template expects 70 tweets. Argument #3 may
40 def __init__(self
, config_file
, screen_name
, count
):
41 """config_file is a string and should point to a configuration file
42 with the necessary credentials for OAUTH. We use ConfigParser to read
43 the configuration file.
45 screen_name is a string and corresponds to the user whose timeline we
48 count is an integer and corresponds to the number of tweets we will
52 self
.config_file
= config_file
53 self
.screen_name
= screen_name
56 def read_config_file(self
):
57 """We use ConfigParser to read the configuration file with the
58 necessary credentials for OAUTH.
61 parser
= SafeConfigParser()
62 parser
.read('config.ini')
64 self
.app_key
= parser
.get('Twitter_OAUTH', 'app_key')
65 self
.app_secret
= parser
.get('Twitter_OAUTH', 'app_secret')
66 self
.oauth_token
= parser
.get('Twitter_OAUTH', 'oauth_token')
67 self
.oauth_token_secret
= parser
.get('Twitter_OAUTH', 'oauth_token_secret')
69 def read_user_timeline(self
):
70 """We connect to twitter, and issue a GET statuses/user_timeline
71 request with self.screen_name and self.count parameters.
73 A response's u'text' field corresponds to a "tweet". See the API 1.1
74 docs for a map of a typical response:
75 https://dev.twitter.com/docs/api/1.1/get/statuses/user_timeline
77 This method's return value is some tweets packaged as list of Unicode
81 twitter
= Twython(app_key
=self
.app_key
, app_secret
=self
.app_secret
,
82 oauth_token
=self
.oauth_token
,
83 oauth_token_secret
=self
.oauth_token_secret
)
85 tweets
= twitter
.getUserTimeline(screen_name
=self
.screen_name
, count
=self
.count
)
89 list_of_tweets
.append(tweet
[u
'text'])
93 class WriteNewTweetsToDB
:
95 """We write some tweets to a database.
97 This class expects two arguments:
98 1. The location of a configuration file with the usual MySQL connection
99 stuff (host, user, pwd, db name). We use ConfigParser to read the
100 configuration file. See config.sample.ini file.
101 2. A list of some tweets as say returned by the read_user_timeline method
102 of ReadNewTweets. The read_user_timeline method returns a list of Unicode
103 type instances, Unicode strings, u'☺☻'
105 The list_of_tweets strings will be written to the database of the
108 This class is not really Twitter-dependent and the strings of Argument #2
113 def __init__(self
, config_file
, list_of_tweets
):
114 """config_file is a string and should point to a configuration file
115 with the usual MySQL connection stuff. See config.sample.ini file...
117 We use ConfigParser to read the configuration file.
119 list_of_tweets is a list as say generated by the read_user_timeline
120 method of the ReadNewTweets class.
123 self
.config_file
= config_file
124 self
.list_of_tweets
= list_of_tweets
126 def read_config_file(self
):
127 """We use ConfigParser to fetch the usual MySQL connection stuff"""
128 parser
= SafeConfigParser()
129 parser
.read(self
.config_file
)
131 self
.db_host
= parser
.get('MySQL', 'host')
132 self
.db_user
= parser
.get('MySQL', 'user')
133 self
.db_password
= parser
.get('MySQL', 'password')
134 self
.db_database
= parser
.get('MySQL', 'database')
136 def write_tweets_to_db(self
):
137 """We write some tweets to the database.
139 We loop over the self.list_of_tweets list, and write each tweet to the
142 For the Unicode tweaks, we read the following:
143 http://dev.mysql.com/doc/refman/5.0/en/charset-applications.html
144 http://stackoverflow.com/questions/8365660/python-mysql-unicode-and-encoding
147 connection
= MySQLdb
.connect(host
= self
.db_host
,
149 passwd
= self
.db_password
,
150 db
= self
.db_database
,
154 cursor
= connection
.cursor()
155 sql_statement
= "CREATE TABLE IF NOT EXISTS tweets (Id INT PRIMARY KEY AUTO_INCREMENT, tweet VARCHAR(140))"
156 cursor
.execute(sql_statement
)
158 for tweet
in self
.list_of_tweets
:
159 cursor
.execute('INSERT INTO tweets(tweet) VALUES(%s)', (tweet
,))
161 class ReadTweetsFromDB
:
163 """We read some tweets from a database and return a list of tweets.
165 This class expects one argument:
166 1. The location of a configuration file with the usual MySQL connection
167 stuff (host, user, pwd, db name). We use ConfigParser to read the
168 configuration file. See config.sample.ini file.
170 The database has been created by say the write_tweets_to_db method of the
171 WriteTweetsToDB class.
173 The read_tweets_from_db method returns a list of tweets...
177 def __init__(self
, config_file
):
178 """config_file is a string and should point to a configuration file
179 with the usual MySQL connection stuff. See config.sample.ini file...
181 We use ConfigParser to read the configuration file.
183 self
.config_file
= config_file
185 def read_config_file(self
):
186 """We use ConfigParser to fetch the usual MySQL connection stuff"""
187 parser
= SafeConfigParser()
188 parser
.read(self
.config_file
)
190 self
.db_host
= parser
.get('MySQL', 'host')
191 self
.db_user
= parser
.get('MySQL', 'user')
192 self
.db_password
= parser
.get('MySQL', 'password')
193 self
.db_database
= parser
.get('MySQL', 'database')
195 def read_tweets_from_db(self
):
196 """We connect to the database and retrieve some tweets.
198 This method's return value is some tweets packaged as list of Unicode
202 connection
= MySQLdb
.connect(host
= self
.db_host
,
204 passwd
= self
.db_password
,
205 db
= self
.db_database
,
209 cursor
= connection
.cursor()
210 cursor
.execute("SELECT * FROM tweets")
212 tweets
= cursor
.fetchall()
215 list_of_tweets
.append(row
[1])
217 return list_of_tweets
219 class WriteTweetsToHTMLFile
:
221 """We display some tweets on a HTML file via Jinja templating.
223 This class expects four arguments:
224 1. A string that points to a location in the file system where Jinja
225 templates are stored. We use the "FileSystemLoader" Jinja loader. The
226 Jinja documentation says: "The loader takes the path to the templates as
227 string" http://jinja.pocoo.org/docs/api/#jinja2.FileSystemLoader
228 2. A string that points to a Jinja template file in the location specified
230 3. A list of some tweets as say returned by the read_tweets_from_db method
231 of ReadTweetsFromDB. The read_user_timeline method returns a list of Unicode
232 type instances, Unicode strings, u'☺☻'. FWIW read_user_timeline of
233 ReadNewTweets returns the same type of tweet lists...
234 4. A string that points to a location in the file system where we can
239 def __init__(self
, template_search_path
, template_name
, list_of_tweets
,
241 """template_search_path is a Jinja templates folder; and template_name
242 is a template therein.
244 list_of_tweets is our list of tweets
246 If it doesn't exist already, the file html_file will be created and
247 our Jinja-generated HTML will be written to it.
250 self
.template_search_path
= template_search_path
251 self
.template_name
= template_name
252 self
.list_of_tweets
= list_of_tweets
253 self
.html_file
= html_file
255 def autolink_list_of_tweets(self
):
256 """We auto link the plain text aspects of tweets. See "Twitter text
258 https://github.com/twitter/twitter-text-conformance
260 We use the twitter-text-py module:
261 pip install twitter-text-py
263 We need to do this in order to render the tweets on the browser...
266 self
.list_of_tweets_autolinked
= []
267 for tweet
in self
.list_of_tweets
:
268 my_autolink
= twitter_text
.Autolink(tweet
)
269 self
.list_of_tweets_autolinked
.append(my_autolink
.auto_link())
271 def write_html_file(self
):
272 """We write the autolinked tweets to a HTML file via Jinja templating.
274 The HTML will be written to a new file. The new file's name is
277 We pass the list_of_tweets_autolinked list to Jinja's render() method
280 See also our Jinja template: template.html
283 env
= Environment(loader
=FileSystemLoader(self
.template_search_path
))
284 template
= env
.get_template(self
.template_name
)
285 f
= codecs
.open(self
.html_file
, mode
="w", encoding
='utf-8')
286 f
.write(template
.render(list_of_tweets
=self
.list_of_tweets_autolinked
))