Completed
Push — master ( 1348df...64fb33 )
by Fox
54s
created

ServiceWallabag._new_token()   B

Complexity

Conditions 3

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
cc 3
c 4
b 1
f 0
dl 0
loc 34
rs 8.8571
1
# coding: utf-8
2
# add here the call of any native lib of python like datetime etc.
3
import arrow
4
import requests
5
from requests import HTTPError
6
# add the python API here if needed
7
from wallabag_api.wallabag import Wallabag as Wall
8
# django classes
9
from django.core.urlresolvers import reverse
10
from logging import getLogger
11
from django.core.cache import caches
12
13
# django_th classes
14
from django_th.services.services import ServicesMgr
15
from django_th.html_entities import HtmlEntities
16
from django_th.models import UserService, ServicesActivated, update_result
17
from th_wallabag.models import Wallabag
18
19
"""
20
    handle process with wallabag
21
    put the following in settings.py
22
23
    TH_SERVICES = (
24
        ...
25
        'th_wallabag.my_wallabag.ServiceWallabag',
26
        ...
27
    )
28
"""
29
30
logger = getLogger('django_th.trigger_happy')
31
32
cache = caches['th_wallabag']
33
34
35
class ServiceWallabag(ServicesMgr):
36
    """
37
        Service Wallabag
38
    """
39
    def __init__(self, token=None, **kwargs):
40
        super(ServiceWallabag, self).__init__(token, **kwargs)
41
        self.token = token
42
        self.user = kwargs.get('user')
43
44
    def _get_wall_data(self):
45
        us = UserService.objects.get(token=self.token, name='ServiceWallabag')
46
47
        params = dict({'access_token': self.token,
48
                       'archive': 0,
49
                       'star': 0,
50
                       'delete': 0,
51
                       'sort': 'created',
52
                       'order': 'desc',
53
                       'page': 1,
54
                       'perPage': 30,
55
                       'tags': []})
56
57
        responses = requests.get(us.host + '/api/entries.json',
58
                                 params=params)
59
        if responses.status_code == 401:
60
            params['access_token'] = self._refresh_token()
61
            responses = requests.get(us.host + '/api/entries.json',
62
                                     params=params)
63
        elif responses.status_code != 200:
64
            raise HTTPError(responses.status_code, responses.json())
65
66
        return responses
67
68
    def read_data(self, **kwargs):
69
        """
70
            get the data from the service
71
            as the pocket service does not have any date
72
            in its API linked to the note,
73
            add the triggered date to the dict data
74
            thus the service will be triggered when data will be found
75
76
            :param kwargs: contain keyword args : trigger_id at least
77
            :type kwargs: dict
78
79
            :rtype: list
80
        """
81
        self.date_triggered = arrow.get(kwargs.get('date_triggered'))
82
        self.trigger_id = kwargs.get('trigger_id')
83
        self.user = kwargs.get('user', '')
84
85
        responses = self._get_wall_data()
86
87
        data = []
88
        try:
89
            json_data = responses.json()
90
91
            for d in json_data['_embedded']['items']:
92
                created_at = arrow.get(d.get('created_at'))
93
                date_triggered = arrow.get(self.date_triggered)
94
95
                if created_at > date_triggered:
96
                    data.append({'title': d.get('title'),
97
                                 'content': d.get('content')})
98
                    # digester
99
                    self.send_digest_event(self.trigger_id,
100
                                           d.get('title'),
101
                                           link='')
102
            if len(data) > 0:
103
                cache.set('th_wallabag_' + str(self.trigger_id), data)
104
        except Exception as e:
105
                logger.critical(e)
106
                update_result(self.trigger_id, msg=e, status=False)
107
        return data
108
109
    def new_wall(self, token):
110
        """
111
            produce a new wall instance
112
            :param token: to get
113
            :return: Wall instance
114
        """
115
        if token:
116
            try:
117
                us = UserService.objects.get(
118
                    token=self.token, name='ServiceWallabag')
119
            except UserService.DoesNotExist:
120
                us = UserService.objects.get(
121
                    user=self.user, name='ServiceWallabag')
122
            finally:
123
                return Wall(host=us.host, client_secret=us.client_secret,
124
                            client_id=us.client_id, token=us.token)
125
126
    def _create_entry(self, title, data, tags):
127
        """
128
            create an entry
129
            :param title: string
130
            :param data: dict
131
            :param tags: list
132
            :return: boolean
133
        """
134
        if data.get('link') and len(data.get('link')) > 0:
135
            wall = self.new_wall(self.token)
136
137
            if wall is None:
138
                err = 'the provided informations for Wallabag are boken - ' \
139
                      'check you login/pass client_id and host'
140
                logger.critical(err)
141
                update_result(self.trigger_id, msg=err, status=False)
142
                return False
143
144
            try:
145
                wall.post_entries(url=data.get('link').encode(),
146
                                  title=title,
147
                                  tags=(tags.lower()))
148
                logger.debug('wallabag {} created'.format(data.get('link')))
149
                status = True
150
            except Exception as e:
151
                    logger.critical('issue with something else that a token'
152
                                    ' link ? : {}'.format(data.get('link')))
153
                    logger.critical(e)
154
                    update_result(self.trigger_id, msg=e, status=False)
155
                    status = False
156
        else:
157
            status = True  # we ignore empty link
158
        return status
159
160
    def _refresh_token(self):
161
        """
162
            refresh the expired token
163
            get the token of the service Wallabag
164
            for the user that uses Wallabag
165
            :return: boolean
166
        """
167
        us = UserService.objects.get(user=self.user, name='ServiceWallabag')
168
        params = {'username': us.username,
169
                  'password': us.password,
170
                  'client_id': us.client_id,
171
                  'client_secret': us.client_secret}
172
        return Wall.get_token(host=us.host, **params)
173
174
    def _new_token(self, userservice_id, link, title, tags):
175
        """
176
            create a new token
177
            :param userservice_id: id of the UserService
178
            :param link: string
179
            :param title: string
180
            :param tags: list
181
            :return: boolean
182
        """
183
        new_token = self._refresh_token()
184
        status = False
185
        if new_token:
186
            logger.info('new token : {}'.format(new_token))
187
            UserService.objects.filter(id=userservice_id).update(
188
                token=new_token)
189
190
            new_wall = self.new_wall(new_token)
191
            try:
192
                status = new_wall.post_entries(url=link, title=title, tags=tags)
193
            except Exception as e:
194
                logger.critical('could not create a post link ? '
195
                                ': {}'.format(link))
196
                logger.critical(e)
197
                update_result(self.trigger_id, msg=e, status=False)
198
                status = False
199
        else:
200
            # when token is False, something is broken with
201
            # login/pass/client_id/client_secret/url
202
            err = 'the provided informations for Wallabag are boken - ' \
203
                  'check you login/pass client_id and host'
204
            logger.critical(err)
205
            update_result(self.trigger_id, msg=err, status=False)
206
207
        return status
208
209
    def save_data(self, trigger_id, **data):
210
        """
211
            let's save the data
212
213
            :param trigger_id: trigger ID from which to save data
214
            :param data: the data to check to be used and save
215
            :type trigger_id: int
216
            :type data:  dict
217
            :return: the status of the save statement
218
            :rtype: boolean
219
        """
220
        self.trigger_id = trigger_id
221
        trigger = Wallabag.objects.get(trigger_id=trigger_id)
222
223
        title = self.set_title(data)
224
        if title is not None:
225
            # convert htmlentities
226
            title = HtmlEntities(title).html_entity_decode
227
228
            return self._create_entry(title, data, trigger.tag)
229
        else:
230
            # we ignore data without title so return True to let
231
            # the process continue without
232
            # raising exception
233
            return True
234
235
    def auth(self, request):
236
        """
237
            let's auth the user to the Service
238
            :param request: request object
239
            :return: callback url
240
            :rtype: string that contains the url to redirect after auth
241
242
        """
243
        service = UserService.objects.get(
244
            user=request.user, name='ServiceWallabag')
245
        callback_url = 'http://%s%s' % (
246
            request.get_host(), reverse('wallabag_callback'))
247
        params = {'username': service.username,
248
                  'password': service.password,
249
                  'client_id': service.client_id,
250
                  'client_secret': service.client_secret}
251
        access_token = Wall.get_token(host=service.host, **params)
252
        request.session['oauth_token'] = access_token
253
        return callback_url
254
255
    def callback(self, request, **kwargs):
256
        """
257
            Called from the Service when the user accept to activate it
258
            :param request: request object
259
            :return: callback url
260
            :rtype: string , path to the template
261
        """
262
263
        try:
264
            UserService.objects.filter(
265
                user=request.user,
266
                name=ServicesActivated.objects.get(name='ServiceWallabag')
267
            )
268
        except KeyError:
269
            return '/'
270
271
        return 'wallabag/callback.html'
272