We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Twitter: truncated text
<!--
This is an issue template. Please fill in the relevant details in the
sections below.
-->

##### Text version
<!-- From menu _Options→Add-ons→Orange3-Text_ or code `orangecontrib.text.version.full_version` -->
0.5.0

##### Orange version
<!-- From menu _Help→About→Version_ or code `Orange.version.full_version` -->
3.17.dev

##### Expected behavior
Twitter retrieves the entire text of tweets.


##### Actual behavior
Twitter truncates text.


##### Steps to reproduce the behavior
Retrieve any sample of tweets.


##### Additional info (worksheets, data, screenshots, ...)
<img width="637" alt="screen shot 2018-09-25 at 14 30 13" src="https://user-images.githubusercontent.com/12524972/46014492-c58e4a00-c0cf-11e8-94f7-f8a12e36cf57.png">




--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `orangecontrib/text/twitter.py`
Content:
```
1 from collections import OrderedDict, Iterable
2 
3 import tweepy
4 
5 from Orange import data
6 from orangecontrib.text import Corpus
7 from orangecontrib.text.language_codes import code2lang
8 
9 __all__ = ['Credentials', 'TwitterAPI']
10 
11 
12 def coordinates_geoJSON(json):
13     if json:
14         return json.get('coordinates', [None, None])
15     return [None, None]
16 
17 
18 class Credentials:
19     """ Twitter API credentials. """
20 
21     def __init__(self, consumer_key, consumer_secret):
22         self.consumer_key = consumer_key
23         self.consumer_secret = consumer_secret
24         self.auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
25         self._valid = None
26 
27     @property
28     def valid(self):
29         if self._valid is None:
30             self.check()
31         return self._valid
32 
33     def check(self):
34         try:
35             self.auth.get_authorization_url()
36             self._valid = True
37         except tweepy.TweepError:
38             self._valid = False
39         return self._valid
40 
41     def __getstate__(self):
42         odict = self.__dict__.copy()
43         odict['_valid'] = None
44         odict.pop('auth')
45         return odict
46 
47     def __setstate__(self, odict):
48         self.__dict__.update(odict)
49         self.auth = tweepy.OAuthHandler(self.consumer_key,
50                                         self.consumer_secret)
51 
52     def __eq__(self, other):
53         return isinstance(other, Credentials) \
54                and self.consumer_key == other.consumer_key \
55                and self.consumer_secret == other.consumer_secret
56 
57 
58 class TwitterAPI:
59     """ Fetch tweets from the Tweeter API.
60 
61     Notes:
62         Results across multiple searches are aggregated. To remove tweets form
63         previous searches and only return results from the last search either
64         call `reset` method before searching or provide `collecting=False`
65         argument to search method.
66     """
67     attributes = []
68     class_vars = [
69         (data.DiscreteVariable('Author'), lambda doc: '@' + doc.author.screen_name),
70     ]
71 
72     tv = data.TimeVariable('Date')
73     metas = [
74         (data.StringVariable('Content'), lambda doc: doc.text),
75         (tv, lambda doc: TwitterAPI.tv.parse(doc.created_at.isoformat())),
76         (data.DiscreteVariable('Language'), lambda doc: doc.lang),
77         (data.DiscreteVariable('Location'), lambda doc: getattr(doc.place, 'country_code', None)),
78         (data.ContinuousVariable('Number of Likes', number_of_decimals=0),
79          lambda doc: doc.favorite_count),
80         (data.ContinuousVariable('Number of Retweets', number_of_decimals=0),
81          lambda doc: doc.retweet_count),
82         (data.DiscreteVariable('In Reply To'),
83             lambda doc: '@' + doc.in_reply_to_screen_name if doc.in_reply_to_screen_name else ''),
84         (data.DiscreteVariable('Author Name'), lambda doc: doc.author.name),
85         (data.StringVariable('Author Description'), lambda doc: doc.author.description),
86         (data.ContinuousVariable('Author Statuses Count', number_of_decimals=0),
87          lambda doc: doc.author.statuses_count),
88         (data.ContinuousVariable('Author Favourites Count', number_of_decimals=0),
89          lambda doc: doc.author.favourites_count),
90         (data.ContinuousVariable('Author Friends Count', number_of_decimals=0),
91          lambda doc: doc.author.friends_count),
92         (data.ContinuousVariable('Author Followers Count', number_of_decimals=0),
93          lambda doc: doc.author.followers_count),
94         (data.ContinuousVariable('Author Listed Count', number_of_decimals=0),
95          lambda doc: doc.author.listed_count),
96         (data.DiscreteVariable('Author Verified'), lambda doc: str(doc.author.verified)),
97         (data.ContinuousVariable('Longitude'),
98             lambda doc: coordinates_geoJSON(doc.coordinates)[0]),
99         (data.ContinuousVariable('Latitude'),
100             lambda doc: coordinates_geoJSON(doc.coordinates)[1]),
101     ]
102 
103     text_features = [metas[0][0]]       # Content
104     string_attributes = [m for m, _ in metas
105                          if isinstance(m, data.StringVariable)]
106 
107     def __init__(self, credentials,
108                  on_progress=None, should_break=None,
109                  on_error=None, on_rate_limit=None):
110         self.key = credentials
111         self.api = tweepy.API(credentials.auth)
112         self.container = OrderedDict()
113         self.search_history = []
114 
115         # Callbacks:
116         self.on_error = on_error
117         self.on_rate_limit = on_rate_limit
118         self.on_progress = on_progress or (lambda *args: args)
119         self.should_break = should_break or (lambda *args: False)
120 
121     @property
122     def tweets(self):
123         return self.container.values()
124 
125     def search_content(self, content, *, max_tweets=0,
126                        lang=None, allow_retweets=True,
127                        collecting=False):
128         """ Search by content.
129 
130         Args:
131             content (list of str): A list of key words to search for.
132             max_tweets (int): If greater than zero limits the number of
133                 downloaded tweets.
134             lang (str): A language's code (either ISO 639-1 or ISO 639-3
135                 formats).
136             allow_retweets(bool): Whether to download retweets.
137             collecting (bool): Whether to collect results across multiple
138                 search calls.
139 
140         Returns:
141             Corpus
142         """
143         if not collecting:
144             self.reset()
145 
146         if max_tweets == 0:
147             max_tweets = float('Inf')
148 
149         def build_query():
150             nonlocal content
151             if not content:
152                 q = 'from: '
153             else:
154                 if not isinstance(content, list):
155                     content = [content]
156                 q = ' OR '.join(['"{}"'.format(q) for q in content])
157             if not allow_retweets:
158                 q += ' -filter:retweets'
159             return q
160 
161         query = build_query()
162         cursor = tweepy.Cursor(self.api.search, q=query, lang=lang)
163         corpus, count = self.fetch(cursor, max_tweets)
164         self.append_history('Content', content, lang if lang else 'Any',
165                             str(allow_retweets), count)
166         return corpus
167 
168     def search_authors(self, authors, *, max_tweets=0, collecting=False):
169         """ Search by authors.
170 
171         Args:
172             authors (list of str): A list of authors to search for.
173             max_tweets (int): If greater than zero limits the number of
174                 downloaded tweets.
175             collecting (bool): Whether to collect results across multiple
176                 search calls.
177 
178         Returns:
179             Corpus
180         """
181         if not collecting:
182             self.reset()
183 
184         if max_tweets == 0:     # set to max allowed for progress
185             max_tweets = 3200
186 
187         if not isinstance(authors, list):
188             authors = [authors]
189 
190         cursors = [tweepy.Cursor(self.api.user_timeline, screen_name=a)
191                    for a in authors]
192         corpus, count = self.fetch(cursors, max_tweets)
193         self.append_history('Author', authors, None, None, count)
194         return corpus
195 
196     def fetch(self, cursors, max_tweets):
197         if not isinstance(cursors, list):
198             cursors = [cursors]
199 
200         count = 0
201         try:
202             for i, cursor in enumerate(cursors):
203                 for j, tweet in enumerate(cursor.items(max_tweets), start=1):
204                     if self.should_break():
205                         break
206                     if tweet.id not in self.container:
207                         count += 1
208                     self.container[tweet.id] = tweet
209                     if j % 20 == 0:
210                         self.on_progress(len(self.container),
211                                          (i*max_tweets + j)/
212                                          (len(cursors)*max_tweets))
213                 if self.should_break():
214                     break
215         except tweepy.TweepError as e:
216             if e.response.status_code == 429 and self.on_rate_limit:
217                 self.on_rate_limit()
218             elif self.on_error:
219                 self.on_error(str(e))
220                 return None, 0
221         return self.create_corpus(), count
222 
223     def create_corpus(self):
224         return Corpus.from_documents(self.tweets, 'Twitter', self.attributes,
225                                      self.class_vars, self.metas,
226                                      title_indices=[-1])
227 
228     def reset(self):
229         """ Removes all downloaded tweets. """
230         self.search_history = []
231         self.container = OrderedDict()
232 
233     def append_history(self, mode, query, lang, allow_retweets, n_tweets):
234         query = ', '.join(query) if isinstance(query, Iterable) else query
235         if lang in code2lang.keys():
236             lang = code2lang[lang]
237         self.search_history.append((
238             ('Query', query),
239             ('Search by', mode),
240             ('Language', lang),
241             ('Allow retweets', allow_retweets),
242             ('Tweets count', n_tweets),
243         ))
244 
245     def report(self):
246         return self.search_history
247
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/orangecontrib/text/twitter.py b/orangecontrib/text/twitter.py
--- a/orangecontrib/text/twitter.py
+++ b/orangecontrib/text/twitter.py
@@ -71,7 +71,7 @@
 
     tv = data.TimeVariable('Date')
     metas = [
-        (data.StringVariable('Content'), lambda doc: doc.text),
+        (data.StringVariable('Content'), lambda doc: doc.full_text),
         (tv, lambda doc: TwitterAPI.tv.parse(doc.created_at.isoformat())),
         (data.DiscreteVariable('Language'), lambda doc: doc.lang),
         (data.DiscreteVariable('Location'), lambda doc: getattr(doc.place, 'country_code', None)),
@@ -159,7 +159,8 @@
             return q
 
         query = build_query()
-        cursor = tweepy.Cursor(self.api.search, q=query, lang=lang)
+        cursor = tweepy.Cursor(self.api.search, q=query, lang=lang,
+                               tweet_mode='extended')
         corpus, count = self.fetch(cursor, max_tweets)
         self.append_history('Content', content, lang if lang else 'Any',
                             str(allow_retweets), count)

{"golden_diff": "diff --git a/orangecontrib/text/twitter.py b/orangecontrib/text/twitter.py\n--- a/orangecontrib/text/twitter.py\n+++ b/orangecontrib/text/twitter.py\n@@ -71,7 +71,7 @@\n \n     tv = data.TimeVariable('Date')\n     metas = [\n-        (data.StringVariable('Content'), lambda doc: doc.text),\n+        (data.StringVariable('Content'), lambda doc: doc.full_text),\n         (tv, lambda doc: TwitterAPI.tv.parse(doc.created_at.isoformat())),\n         (data.DiscreteVariable('Language'), lambda doc: doc.lang),\n         (data.DiscreteVariable('Location'), lambda doc: getattr(doc.place, 'country_code', None)),\n@@ -159,7 +159,8 @@\n             return q\n \n         query = build_query()\n-        cursor = tweepy.Cursor(self.api.search, q=query, lang=lang)\n+        cursor = tweepy.Cursor(self.api.search, q=query, lang=lang,\n+                               tweet_mode='extended')\n         corpus, count = self.fetch(cursor, max_tweets)\n         self.append_history('Content', content, lang if lang else 'Any',\n                             str(allow_retweets), count)\n", "issue": "Twitter: truncated text\n<!--\r\nThis is an issue template. Please fill in the relevant details in the\r\nsections below.\r\n-->\r\n\r\n##### Text version\r\n<!-- From menu _Options\u2192Add-ons\u2192Orange3-Text_ or code `orangecontrib.text.version.full_version` -->\r\n0.5.0\r\n\r\n##### Orange version\r\n<!-- From menu _Help\u2192About\u2192Version_ or code `Orange.version.full_version` -->\r\n3.17.dev\r\n\r\n##### Expected behavior\r\nTwitter retrieves the entire text of tweets.\r\n\r\n\r\n##### Actual behavior\r\nTwitter truncates text.\r\n\r\n\r\n##### Steps to reproduce the behavior\r\nRetrieve any sample of tweets.\r\n\r\n\r\n##### Additional info (worksheets, data, screenshots, ...)\r\n<img width=\"637\" alt=\"screen shot 2018-09-25 at 14 30 13\" src=\"https://user-images.githubusercontent.com/12524972/46014492-c58e4a00-c0cf-11e8-94f7-f8a12e36cf57.png\">\r\n\r\n\r\n\n", "before_files": [{"content": "from collections import OrderedDict, Iterable\n\nimport tweepy\n\nfrom Orange import data\nfrom orangecontrib.text import Corpus\nfrom orangecontrib.text.language_codes import code2lang\n\n__all__ = ['Credentials', 'TwitterAPI']\n\n\ndef coordinates_geoJSON(json):\n    if json:\n        return json.get('coordinates', [None, None])\n    return [None, None]\n\n\nclass Credentials:\n    \"\"\" Twitter API credentials. \"\"\"\n\n    def __init__(self, consumer_key, consumer_secret):\n        self.consumer_key = consumer_key\n        self.consumer_secret = consumer_secret\n        self.auth = tweepy.OAuthHandler(consumer_key, consumer_secret)\n        self._valid = None\n\n    @property\n    def valid(self):\n        if self._valid is None:\n            self.check()\n        return self._valid\n\n    def check(self):\n        try:\n            self.auth.get_authorization_url()\n            self._valid = True\n        except tweepy.TweepError:\n            self._valid = False\n        return self._valid\n\n    def __getstate__(self):\n        odict = self.__dict__.copy()\n        odict['_valid'] = None\n        odict.pop('auth')\n        return odict\n\n    def __setstate__(self, odict):\n        self.__dict__.update(odict)\n        self.auth = tweepy.OAuthHandler(self.consumer_key,\n                                        self.consumer_secret)\n\n    def __eq__(self, other):\n        return isinstance(other, Credentials) \\\n               and self.consumer_key == other.consumer_key \\\n               and self.consumer_secret == other.consumer_secret\n\n\nclass TwitterAPI:\n    \"\"\" Fetch tweets from the Tweeter API.\n\n    Notes:\n        Results across multiple searches are aggregated. To remove tweets form\n        previous searches and only return results from the last search either\n        call `reset` method before searching or provide `collecting=False`\n        argument to search method.\n    \"\"\"\n    attributes = []\n    class_vars = [\n        (data.DiscreteVariable('Author'), lambda doc: '@' + doc.author.screen_name),\n    ]\n\n    tv = data.TimeVariable('Date')\n    metas = [\n        (data.StringVariable('Content'), lambda doc: doc.text),\n        (tv, lambda doc: TwitterAPI.tv.parse(doc.created_at.isoformat())),\n        (data.DiscreteVariable('Language'), lambda doc: doc.lang),\n        (data.DiscreteVariable('Location'), lambda doc: getattr(doc.place, 'country_code', None)),\n        (data.ContinuousVariable('Number of Likes', number_of_decimals=0),\n         lambda doc: doc.favorite_count),\n        (data.ContinuousVariable('Number of Retweets', number_of_decimals=0),\n         lambda doc: doc.retweet_count),\n        (data.DiscreteVariable('In Reply To'),\n            lambda doc: '@' + doc.in_reply_to_screen_name if doc.in_reply_to_screen_name else ''),\n        (data.DiscreteVariable('Author Name'), lambda doc: doc.author.name),\n        (data.StringVariable('Author Description'), lambda doc: doc.author.description),\n        (data.ContinuousVariable('Author Statuses Count', number_of_decimals=0),\n         lambda doc: doc.author.statuses_count),\n        (data.ContinuousVariable('Author Favourites Count', number_of_decimals=0),\n         lambda doc: doc.author.favourites_count),\n        (data.ContinuousVariable('Author Friends Count', number_of_decimals=0),\n         lambda doc: doc.author.friends_count),\n        (data.ContinuousVariable('Author Followers Count', number_of_decimals=0),\n         lambda doc: doc.author.followers_count),\n        (data.ContinuousVariable('Author Listed Count', number_of_decimals=0),\n         lambda doc: doc.author.listed_count),\n        (data.DiscreteVariable('Author Verified'), lambda doc: str(doc.author.verified)),\n        (data.ContinuousVariable('Longitude'),\n            lambda doc: coordinates_geoJSON(doc.coordinates)[0]),\n        (data.ContinuousVariable('Latitude'),\n            lambda doc: coordinates_geoJSON(doc.coordinates)[1]),\n    ]\n\n    text_features = [metas[0][0]]       # Content\n    string_attributes = [m for m, _ in metas\n                         if isinstance(m, data.StringVariable)]\n\n    def __init__(self, credentials,\n                 on_progress=None, should_break=None,\n                 on_error=None, on_rate_limit=None):\n        self.key = credentials\n        self.api = tweepy.API(credentials.auth)\n        self.container = OrderedDict()\n        self.search_history = []\n\n        # Callbacks:\n        self.on_error = on_error\n        self.on_rate_limit = on_rate_limit\n        self.on_progress = on_progress or (lambda *args: args)\n        self.should_break = should_break or (lambda *args: False)\n\n    @property\n    def tweets(self):\n        return self.container.values()\n\n    def search_content(self, content, *, max_tweets=0,\n                       lang=None, allow_retweets=True,\n                       collecting=False):\n        \"\"\" Search by content.\n\n        Args:\n            content (list of str): A list of key words to search for.\n            max_tweets (int): If greater than zero limits the number of\n                downloaded tweets.\n            lang (str): A language's code (either ISO 639-1 or ISO 639-3\n                formats).\n            allow_retweets(bool): Whether to download retweets.\n            collecting (bool): Whether to collect results across multiple\n                search calls.\n\n        Returns:\n            Corpus\n        \"\"\"\n        if not collecting:\n            self.reset()\n\n        if max_tweets == 0:\n            max_tweets = float('Inf')\n\n        def build_query():\n            nonlocal content\n            if not content:\n                q = 'from: '\n            else:\n                if not isinstance(content, list):\n                    content = [content]\n                q = ' OR '.join(['\"{}\"'.format(q) for q in content])\n            if not allow_retweets:\n                q += ' -filter:retweets'\n            return q\n\n        query = build_query()\n        cursor = tweepy.Cursor(self.api.search, q=query, lang=lang)\n        corpus, count = self.fetch(cursor, max_tweets)\n        self.append_history('Content', content, lang if lang else 'Any',\n                            str(allow_retweets), count)\n        return corpus\n\n    def search_authors(self, authors, *, max_tweets=0, collecting=False):\n        \"\"\" Search by authors.\n\n        Args:\n            authors (list of str): A list of authors to search for.\n            max_tweets (int): If greater than zero limits the number of\n                downloaded tweets.\n            collecting (bool): Whether to collect results across multiple\n                search calls.\n\n        Returns:\n            Corpus\n        \"\"\"\n        if not collecting:\n            self.reset()\n\n        if max_tweets == 0:     # set to max allowed for progress\n            max_tweets = 3200\n\n        if not isinstance(authors, list):\n            authors = [authors]\n\n        cursors = [tweepy.Cursor(self.api.user_timeline, screen_name=a)\n                   for a in authors]\n        corpus, count = self.fetch(cursors, max_tweets)\n        self.append_history('Author', authors, None, None, count)\n        return corpus\n\n    def fetch(self, cursors, max_tweets):\n        if not isinstance(cursors, list):\n            cursors = [cursors]\n\n        count = 0\n        try:\n            for i, cursor in enumerate(cursors):\n                for j, tweet in enumerate(cursor.items(max_tweets), start=1):\n                    if self.should_break():\n                        break\n                    if tweet.id not in self.container:\n                        count += 1\n                    self.container[tweet.id] = tweet\n                    if j % 20 == 0:\n                        self.on_progress(len(self.container),\n                                         (i*max_tweets + j)/\n                                         (len(cursors)*max_tweets))\n                if self.should_break():\n                    break\n        except tweepy.TweepError as e:\n            if e.response.status_code == 429 and self.on_rate_limit:\n                self.on_rate_limit()\n            elif self.on_error:\n                self.on_error(str(e))\n                return None, 0\n        return self.create_corpus(), count\n\n    def create_corpus(self):\n        return Corpus.from_documents(self.tweets, 'Twitter', self.attributes,\n                                     self.class_vars, self.metas,\n                                     title_indices=[-1])\n\n    def reset(self):\n        \"\"\" Removes all downloaded tweets. \"\"\"\n        self.search_history = []\n        self.container = OrderedDict()\n\n    def append_history(self, mode, query, lang, allow_retweets, n_tweets):\n        query = ', '.join(query) if isinstance(query, Iterable) else query\n        if lang in code2lang.keys():\n            lang = code2lang[lang]\n        self.search_history.append((\n            ('Query', query),\n            ('Search by', mode),\n            ('Language', lang),\n            ('Allow retweets', allow_retweets),\n            ('Tweets count', n_tweets),\n        ))\n\n    def report(self):\n        return self.search_history\n", "path": "orangecontrib/text/twitter.py"}], "after_files": [{"content": "from collections import OrderedDict, Iterable\n\nimport tweepy\n\nfrom Orange import data\nfrom orangecontrib.text import Corpus\nfrom orangecontrib.text.language_codes import code2lang\n\n__all__ = ['Credentials', 'TwitterAPI']\n\n\ndef coordinates_geoJSON(json):\n    if json:\n        return json.get('coordinates', [None, None])\n    return [None, None]\n\n\nclass Credentials:\n    \"\"\" Twitter API credentials. \"\"\"\n\n    def __init__(self, consumer_key, consumer_secret):\n        self.consumer_key = consumer_key\n        self.consumer_secret = consumer_secret\n        self.auth = tweepy.OAuthHandler(consumer_key, consumer_secret)\n        self._valid = None\n\n    @property\n    def valid(self):\n        if self._valid is None:\n            self.check()\n        return self._valid\n\n    def check(self):\n        try:\n            self.auth.get_authorization_url()\n            self._valid = True\n        except tweepy.TweepError:\n            self._valid = False\n        return self._valid\n\n    def __getstate__(self):\n        odict = self.__dict__.copy()\n        odict['_valid'] = None\n        odict.pop('auth')\n        return odict\n\n    def __setstate__(self, odict):\n        self.__dict__.update(odict)\n        self.auth = tweepy.OAuthHandler(self.consumer_key,\n                                        self.consumer_secret)\n\n    def __eq__(self, other):\n        return isinstance(other, Credentials) \\\n               and self.consumer_key == other.consumer_key \\\n               and self.consumer_secret == other.consumer_secret\n\n\nclass TwitterAPI:\n    \"\"\" Fetch tweets from the Tweeter API.\n\n    Notes:\n        Results across multiple searches are aggregated. To remove tweets form\n        previous searches and only return results from the last search either\n        call `reset` method before searching or provide `collecting=False`\n        argument to search method.\n    \"\"\"\n    attributes = []\n    class_vars = [\n        (data.DiscreteVariable('Author'), lambda doc: '@' + doc.author.screen_name),\n    ]\n\n    tv = data.TimeVariable('Date')\n    metas = [\n        (data.StringVariable('Content'), lambda doc: doc.full_text),\n        (tv, lambda doc: TwitterAPI.tv.parse(doc.created_at.isoformat())),\n        (data.DiscreteVariable('Language'), lambda doc: doc.lang),\n        (data.DiscreteVariable('Location'), lambda doc: getattr(doc.place, 'country_code', None)),\n        (data.ContinuousVariable('Number of Likes', number_of_decimals=0),\n         lambda doc: doc.favorite_count),\n        (data.ContinuousVariable('Number of Retweets', number_of_decimals=0),\n         lambda doc: doc.retweet_count),\n        (data.DiscreteVariable('In Reply To'),\n            lambda doc: '@' + doc.in_reply_to_screen_name if doc.in_reply_to_screen_name else ''),\n        (data.DiscreteVariable('Author Name'), lambda doc: doc.author.name),\n        (data.StringVariable('Author Description'), lambda doc: doc.author.description),\n        (data.ContinuousVariable('Author Statuses Count', number_of_decimals=0),\n         lambda doc: doc.author.statuses_count),\n        (data.ContinuousVariable('Author Favourites Count', number_of_decimals=0),\n         lambda doc: doc.author.favourites_count),\n        (data.ContinuousVariable('Author Friends Count', number_of_decimals=0),\n         lambda doc: doc.author.friends_count),\n        (data.ContinuousVariable('Author Followers Count', number_of_decimals=0),\n         lambda doc: doc.author.followers_count),\n        (data.ContinuousVariable('Author Listed Count', number_of_decimals=0),\n         lambda doc: doc.author.listed_count),\n        (data.DiscreteVariable('Author Verified'), lambda doc: str(doc.author.verified)),\n        (data.ContinuousVariable('Longitude'),\n            lambda doc: coordinates_geoJSON(doc.coordinates)[0]),\n        (data.ContinuousVariable('Latitude'),\n            lambda doc: coordinates_geoJSON(doc.coordinates)[1]),\n    ]\n\n    text_features = [metas[0][0]]       # Content\n    string_attributes = [m for m, _ in metas\n                         if isinstance(m, data.StringVariable)]\n\n    def __init__(self, credentials,\n                 on_progress=None, should_break=None,\n                 on_error=None, on_rate_limit=None):\n        self.key = credentials\n        self.api = tweepy.API(credentials.auth)\n        self.container = OrderedDict()\n        self.search_history = []\n\n        # Callbacks:\n        self.on_error = on_error\n        self.on_rate_limit = on_rate_limit\n        self.on_progress = on_progress or (lambda *args: args)\n        self.should_break = should_break or (lambda *args: False)\n\n    @property\n    def tweets(self):\n        return self.container.values()\n\n    def search_content(self, content, *, max_tweets=0,\n                       lang=None, allow_retweets=True,\n                       collecting=False):\n        \"\"\" Search by content.\n\n        Args:\n            content (list of str): A list of key words to search for.\n            max_tweets (int): If greater than zero limits the number of\n                downloaded tweets.\n            lang (str): A language's code (either ISO 639-1 or ISO 639-3\n                formats).\n            allow_retweets(bool): Whether to download retweets.\n            collecting (bool): Whether to collect results across multiple\n                search calls.\n\n        Returns:\n            Corpus\n        \"\"\"\n        if not collecting:\n            self.reset()\n\n        if max_tweets == 0:\n            max_tweets = float('Inf')\n\n        def build_query():\n            nonlocal content\n            if not content:\n                q = 'from: '\n            else:\n                if not isinstance(content, list):\n                    content = [content]\n                q = ' OR '.join(['\"{}\"'.format(q) for q in content])\n            if not allow_retweets:\n                q += ' -filter:retweets'\n            return q\n\n        query = build_query()\n        cursor = tweepy.Cursor(self.api.search, q=query, lang=lang,\n                               tweet_mode='extended')\n        corpus, count = self.fetch(cursor, max_tweets)\n        self.append_history('Content', content, lang if lang else 'Any',\n                            str(allow_retweets), count)\n        return corpus\n\n    def search_authors(self, authors, *, max_tweets=0, collecting=False):\n        \"\"\" Search by authors.\n\n        Args:\n            authors (list of str): A list of authors to search for.\n            max_tweets (int): If greater than zero limits the number of\n                downloaded tweets.\n            collecting (bool): Whether to collect results across multiple\n                search calls.\n\n        Returns:\n            Corpus\n        \"\"\"\n        if not collecting:\n            self.reset()\n\n        if max_tweets == 0:     # set to max allowed for progress\n            max_tweets = 3200\n\n        if not isinstance(authors, list):\n            authors = [authors]\n\n        cursors = [tweepy.Cursor(self.api.user_timeline, screen_name=a)\n                   for a in authors]\n        corpus, count = self.fetch(cursors, max_tweets)\n        self.append_history('Author', authors, None, None, count)\n        return corpus\n\n    def fetch(self, cursors, max_tweets):\n        if not isinstance(cursors, list):\n            cursors = [cursors]\n\n        count = 0\n        try:\n            for i, cursor in enumerate(cursors):\n                for j, tweet in enumerate(cursor.items(max_tweets), start=1):\n                    if self.should_break():\n                        break\n                    if tweet.id not in self.container:\n                        count += 1\n                    self.container[tweet.id] = tweet\n                    if j % 20 == 0:\n                        self.on_progress(len(self.container),\n                                         (i*max_tweets + j)/\n                                         (len(cursors)*max_tweets))\n                if self.should_break():\n                    break\n        except tweepy.TweepError as e:\n            if e.response.status_code == 429 and self.on_rate_limit:\n                self.on_rate_limit()\n            elif self.on_error:\n                self.on_error(str(e))\n                return None, 0\n        return self.create_corpus(), count\n\n    def create_corpus(self):\n        return Corpus.from_documents(self.tweets, 'Twitter', self.attributes,\n                                     self.class_vars, self.metas,\n                                     title_indices=[-1])\n\n    def reset(self):\n        \"\"\" Removes all downloaded tweets. \"\"\"\n        self.search_history = []\n        self.container = OrderedDict()\n\n    def append_history(self, mode, query, lang, allow_retweets, n_tweets):\n        query = ', '.join(query) if isinstance(query, Iterable) else query\n        if lang in code2lang.keys():\n            lang = code2lang[lang]\n        self.search_history.append((\n            ('Query', query),\n            ('Search by', mode),\n            ('Language', lang),\n            ('Allow retweets', allow_retweets),\n            ('Tweets count', n_tweets),\n        ))\n\n    def report(self):\n        return self.search_history\n", "path": "orangecontrib/text/twitter.py"}]}