Projet

Général

Profil

0003-remove-support-for-clicrdv-5484.patch

Frédéric Péters, 26 novembre 2019 14:16

Télécharger (19,8 ko)

Voir les différences:

Subject: [PATCH 3/6] remove support for clicrdv (#5484)

 auquotidien/modules/admin.py      |  58 +----
 auquotidien/modules/clicrdv.py    | 365 ------------------------------
 auquotidien/modules/connectors.py |   1 -
 3 files changed, 1 insertion(+), 423 deletions(-)
 delete mode 100644 auquotidien/modules/clicrdv.py
auquotidien/modules/admin.py
23 23

  
24 24
class PanelDirectory(Directory):
25 25
    _q_exports = ['', 'update', 'announces', 'permissions',
26
            'announce_themes', 'strongbox', 'clicrdv', 'domino']
26
            'announce_themes', 'strongbox', 'domino']
27 27
    label = N_('Control Panel')
28 28

  
29 29
    domino = AbeliumDominoDirectory()
......
160 160
            cfg_submit(form, 'misc', ('aq-strongbox',))
161 161
            return redirect('..')
162 162

  
163
    def clicrdv(self):
164
        if not get_publisher().has_site_option('clicrdv'):
165
            raise errors.TraversalError()
166
        misc_cfg = get_cfg('misc', {})
167
        form = Form(enctype='multipart/form-data')
168
        form.add(SingleSelectWidget, 'aq-clicrdv-server', title=_('ClicRDV Server'),
169
                value=misc_cfg.get('aq-clicrdv-server', 'sandbox.clicrdv.com'), required=True,
170
                options=[(str('www.clicrdv.com'), _('Production Server')),
171
                         (str('sandbox.clicrdv.com'), _('Sandbox Server'))])
172
        form.add(StringWidget, 'aq-clicrdv-api-key', title=_('API Key'),
173
                value=misc_cfg.get('aq-clicrdv-api-key'), required=False,
174
                size=40, hint=_('Empty to disable ClicRDV support'))
175
        form.add(StringWidget, 'aq-clicrdv-api-username', title=_('Username'),
176
                value=misc_cfg.get('aq-clicrdv-api-username'), required=False)
177
        form.add(StringWidget, 'aq-clicrdv-api-password', title=_('Password'),
178
                value=misc_cfg.get('aq-clicrdv-api-password'), required=False)
179

  
180
        form.add_submit('submit', _('Submit'))
181
        form.add_submit('cancel', _('Cancel'))
182

  
183
        if form.get_widget('cancel').parse():
184
            return redirect('..')
185

  
186
        if not form.is_submitted() or form.has_errors():
187
            get_response().breadcrumb.append(('aq/clicrdv', _('ClicRDV Integration')))
188
            html_top('settings', _('ClicRDV Integration'))
189
            r = TemplateIO(html=True)
190
            r += htmltext('<h2>%s</h2>') % _('ClicRDV Integration')
191
            r += form.render()
192
            r += htmltext('<p>%s</p>') % _('Available Interventions: ')
193
            try:
194
                from clicrdv import get_all_intervention_sets
195
                intervention_sets = get_all_intervention_sets()
196
                r += htmltext('<ul>')
197
                for s in intervention_sets:
198
                    r += htmltext('<li><strong>clicrdv_get_interventions_in_set(%s)</strong> - %s') % (
199
                            s['id'], s['name'])
200
                    r += htmltext('<ul>')
201
                    for n, intervention in s['interventions']:
202
                        r += htmltext('<li>%s (id: %s)</li>') % (intervention, n)
203
                    r += htmltext('</ul></li>')
204
                r += htmltext('</ul>')
205
            except Exception, e:
206
                r += htmltext('<p>%s (%s)</p>') % (
207
                        _('Cannot access to ClicRDV service'), str(e))
208
            return r.getvalue()
209
        else:
210
            from wcs.admin.settings import cfg_submit
211
            cfg_submit(form, 'misc', ('aq-clicrdv-server',
212
                                      'aq-clicrdv-api-key',
213
                                      'aq-clicrdv-api-username',
214
                                      'aq-clicrdv-api-password'))
215
            return redirect('..')
216

  
217 163

  
218 164
class SettingsDirectory(wcs.admin.settings.SettingsDirectory):
219 165
    def _q_index(self):
......
234 180
            r += htmltext('<li><a href="aq/announce_themes">%s</a></li>') % _('Announce Themes')
235 181
        if get_publisher().has_site_option('strongbox'):
236 182
            r += htmltext('<li><a href="aq/strongbox">%s</a></li>') % _('Strongbox Support')
237
        if get_publisher().has_site_option('clicrdv'):
238
            r += htmltext('<li><a href="aq/clicrdv">%s</a></li>') % _('ClicRDV Integration')
239 183
        if get_publisher().has_site_option('domino'):
240 184
            r += htmltext('<li><a href="aq/domino">%s</a></li>') % _('Abelium Domino Integration')
241 185
        r += htmltext('</ul>')
auquotidien/modules/clicrdv.py
1
import base64
2
import datetime
3
import urllib2
4

  
5
try:
6
    import json
7
except ImportError:
8
    import simplejson as json
9

  
10
import time
11
import vobject
12

  
13
from wcs.qommon import _
14
from wcs.qommon import get_cfg
15
from wcs.qommon.misc import format_time
16
from wcs.qommon.form import *
17

  
18
from wcs.data_sources import register_data_source_function
19
from wcs.formdata import Evolution
20
from wcs.forms.common import FormStatusPage
21
from wcs.workflows import Workflow, WorkflowStatusItem, register_item_class
22

  
23
def get_clicrdv_req(url):
24
    misc_cfg = get_cfg('misc', {})
25

  
26
    url = 'https://%s/api/v1/%s' % (
27
                misc_cfg.get('aq-clicrdv-server', 'sandbox.clicrdv.com'), url)
28
    if '?' in url:
29
       url = url + '&apikey=%s&format=json' % misc_cfg.get('aq-clicrdv-api-key')
30
    else:
31
       url = url + '?apikey=%s&format=json' % misc_cfg.get('aq-clicrdv-api-key')
32

  
33
    req = urllib2.Request(url)
34
    username = misc_cfg.get('aq-clicrdv-api-username')
35
    password = misc_cfg.get('aq-clicrdv-api-password')
36
    authheader = 'Basic ' + base64.encodestring('%s:%s' % (username, password))[:-1]
37
    req.add_header('Authorization', authheader)
38
    return req
39

  
40
def get_json(url):
41
    return json.load(urllib2.urlopen(get_clicrdv_req(url)))
42

  
43
def as_str(s):
44
    if type(s) is unicode:
45
        return s.encode(get_publisher().site_charset)
46
    return s
47

  
48
def get_all_intervention_sets():
49
    interventions_set = []
50
    for interventionset in sorted(get_json('interventionsets').get('records'), 
51
            lambda x,y: cmp(x['sort'],y['sort'])):
52
        interventions = []
53
        for intervention in sorted(get_json('interventionsets/%s/interventions' % interventionset.get('id')).get('records'),
54
                lambda x,y: cmp(x['sort'], y['sort'])):
55
            if intervention.get('deleted') == True:
56
                continue
57
            name = '%s' % as_str(intervention.get('publicname'))
58
            if not name:
59
                name = '%s' % as_str(intervention.get('name'))
60
            interventions.append((intervention.get('id'), as_str(name)))
61
        interventions_set.append({
62
                'id': interventionset.get('id'),
63
                'group_id': interventionset.get('group_id'),
64
                'name': as_str(interventionset.get('name')),
65
                'publicname': as_str(interventionset.get('publicname')) or '',
66
                'description': as_str(interventionset.get('description')) or '',
67
                'interventions': interventions
68
                })
69
    return interventions_set
70

  
71
def get_all_interventions():
72
    interventions = []
73
    for s in get_all_intervention_sets():
74
        for i, publicname in s['interventions']:
75
            intervention_label = '%s - %s' % (s['publicname'], publicname)
76
            interventions.append((i, as_str(intervention_label)))
77
    return interventions
78

  
79
def get_interventions_in_set(interventionset_id):
80
    interventions = []
81
    interventions_json = get_json('interventionsets/%s/interventions' % interventionset_id)
82
    for intervention in interventions_json.get('records'):
83
        if intervention.get('deleted') != True:
84
            name = '%s' % as_str(intervention.get('publicname'))
85
            if not name:
86
                name = '%s' % as_str(intervention.get('name'))
87
            interventions.append((intervention.get('id'), name))
88
    return interventions
89

  
90
def get_available_timeslots(intervention, date_start=None, date_end=None):
91
    timeslots = []
92
    iid = intervention
93
    gid = get_json('interventions/%s' % iid).get('group_id')
94
    request_url = 'availabletimeslots?intervention_ids[]=%s&group_id=%s' % (iid, gid)
95
    if date_start is None:
96
        date_start = datetime.datetime.today().strftime('%Y-%m-%d')
97
    if date_end is None:
98
        date_end = (datetime.datetime.today() + datetime.timedelta(366)).strftime('%Y-%m-%d')
99
    if date_start:
100
        request_url = request_url + '&start=%s' % urllib2.quote(date_start)
101
    if date_end:
102
        request_url = request_url + '&end=%s' % urllib2.quote(date_end)
103
    for timeslot in get_json(request_url).get('availabletimeslots'):
104
        timeslots.append(timeslot.get('start'))
105
    timeslots.sort()
106
    return timeslots
107

  
108
def get_available_dates(intervention):
109
    dates = []
110
    for timeslot in get_available_timeslots(intervention):
111
        parsed = time.strptime(timeslot, '%Y-%m-%d %H:%M:%S')
112
        date_tuple = (time.strftime('%Y-%m-%d', parsed),
113
                      format_time(parsed, '%(weekday_name)s %(day)0.2d/%(month)0.2d/%(year)s'))
114
        if date_tuple in dates:
115
            continue
116
        dates.append(date_tuple)
117
    return dates
118

  
119
def get_available_times(intervention, date):
120
    times = []
121
    timeslots = get_available_timeslots(intervention,
122
                    date_start='%s 00:00:00' % date,
123
                    date_end='%s 23:59:59' % date)
124
    for timeslot in timeslots:
125
        parsed = time.strptime(timeslot, '%Y-%m-%d %H:%M:%S')
126
        time_tuple = (time.strftime('%H:%M:%S', parsed),
127
                      time.strftime('%Hh%M', parsed))
128
        times.append(time_tuple)
129
    times.sort()
130
    return times
131

  
132
register_data_source_function(get_all_interventions, 'clicrdv_get_all_interventions')
133
register_data_source_function(get_interventions_in_set, 'clicrdv_get_interventions_in_set')
134
register_data_source_function(get_available_dates, 'clicrdv_get_available_dates')
135
register_data_source_function(get_available_times, 'clicrdv_get_available_times')
136

  
137
def form_download_event(self):
138
    self.check_receiver()
139

  
140
    found = False
141
    for evo in self.filled.evolution:
142
        if evo.parts:
143
            for p in evo.parts:
144
                if not isinstance(p, AppointmentPart):
145
                    continue
146
                cal = vobject.iCalendar()
147
                cal.add('prodid').value = '-//Entr\'ouvert//NON SGML Publik'
148
                vevent = vobject.newFromBehavior('vevent')
149
                vevent.add('uid').value = 'clicrdv-%s' % p.id
150
                vevent.add('summary').value = p.json_dict.get('group_name')
151
                vevent.add('dtstart').value = datetime.datetime.strptime(
152
                                p.json_dict.get('start'), '%Y-%m-%d %H:%M:%S')
153
                vevent.add('dtend').value = datetime.datetime.strptime(
154
                                p.json_dict.get('end'), '%Y-%m-%d %H:%M:%S')
155
                vevent.add('location').value = p.json_dict.get('location')
156
                cal.add(vevent)
157

  
158
                response = get_response()
159
                response.set_content_type('text/calendar')
160
                return cal.serialize()
161

  
162
    raise TraversalError()
163

  
164

  
165
class AppointmentPart(object):
166
    def __init__(self, json_dict):
167
        self.id = json_dict.get('id')
168
        self.json_dict = json_dict
169

  
170
    def view(self):
171
        return htmltext('<p class="appointment"><a href="clicrdvevent">%s</a></p>' % (
172
                                _('Download Appointment')))
173

  
174

  
175
class AppointmentErrorPart(object):
176
    def __init__(self, msg):
177
        self.msg = msg
178

  
179
    def view(self):
180
        return htmltext('<p class="appointment-error">%s</p>' % str(self.msg))
181

  
182

  
183
class ClicRdvCreateAppointment(WorkflowStatusItem):
184
    description = N_('Create a ClicRDV Appointment')
185
    key = 'clicrdv-create'
186
    category = 'interaction'
187

  
188
    endpoint = False
189

  
190
    var_firstname = None
191
    var_lastname = None
192
    var_email = None
193
    var_firstphone = None
194
    var_secondphone = None
195
    var_datetime = None
196
    var_intervention_id = None
197
    status_on_success = None
198
    status_on_failure = None
199

  
200
    def init(cls):
201
        FormStatusPage._q_extra_exports.append('clicrdvevent')
202
        FormStatusPage.clicrdvevent = form_download_event
203
    init = classmethod(init)
204

  
205
    def is_available(self, workflow=None):
206
        return get_publisher().has_site_option('clicrdv')
207
    is_available = classmethod(is_available)
208

  
209
    def render_as_line(self):
210
        return _('Create an appointment in ClicRDV')
211

  
212
    def get_parameters(self):
213
        return ('var_firstname', 'var_lastname', 'var_email', 'var_firstphone',
214
                'var_secondphone', 'var_datetime', 'var_intervention_id',
215
                'status_on_success', 'status_on_failure')
216

  
217
    def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
218
        parameter_labels = {
219
            'var_firstname': N_('First Name'),
220
            'var_lastname': N_('Last Name'),
221
            'var_email': N_('Email'),
222
            'var_firstphone': N_('Phone (1st)'),
223
            'var_secondphone':  N_('Phone (2nd)'),
224
            'var_datetime': N_('Date/time'),
225
            'var_intervention_id': N_('Intervention Id'),
226
        }
227
        for parameter in self.get_parameters():
228
            if not parameter in parameter_labels:
229
                continue
230
            if parameter in parameters:
231
                form.add(StringWidget, '%s%s' % (prefix, parameter),
232
                         title=_(parameter_labels.get(parameter)),
233
                         value=getattr(self, parameter),
234
                         required=False)
235
        if 'status_on_success' in parameters:
236
            form.add(SingleSelectWidget, '%sstatus_on_success' % prefix,
237
                    title=_('Status On Success'), value=self.status_on_success,
238
                    options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status])
239
        if 'status_on_failure' in parameters:
240
            form.add(SingleSelectWidget, '%sstatus_on_failure' % prefix,
241
                    title=_('Status On Failure'), value=self.status_on_failure,
242
                    options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status])
243

  
244
    def perform(self, formdata):
245
        args = {}
246
        for parameter in self.get_parameters():
247
            args[parameter] = self.compute(getattr(self, parameter))
248
            if not args.get(parameter):
249
                del args[parameter]
250
        message = {'appointment':
251
                      {'fiche': {'firstname': args.get('var_firstname', '-'),
252
                                 'lastname': args.get('var_lastname', '-'),
253
                                 'email': args.get('var_email'),
254
                                 'firstphone': args.get('var_firstphone'),
255
                                 'secondphone': args.get('var_secondphone'),
256
                                },
257
                       'date': args.get('var_datetime'),
258
                       'intervention_ids': [int(args.get('var_intervention_id'))],
259
                       # 'comments': '-',
260
                       'websource': 'Publik'}
261
                  }
262

  
263
        req = get_clicrdv_req('appointments')
264
        req.add_data(json.dumps(message))
265
        req.add_header('Content-Type', 'application/json')
266

  
267
        try:
268
            fd = urllib2.urlopen(req)
269
        except urllib2.HTTPError, e:
270
            success = False
271
            try:
272
                msg = json.load(e.fp)[0].get('error')
273
            except:
274
                msg = _('unknown error')
275

  
276
            if formdata.evolution:
277
                evo = formdata.evolution[-1]
278
            else:
279
                formdata.evolution = []
280
                evo = Evolution()
281
                evo.time = time.localtime()
282
                evo.status = formdata.status
283
                formdata.evolution.append(evo)
284
            evo.add_part(AppointmentErrorPart(msg))
285
        else:
286
            success = True
287
            response = json.load(fd)
288
            appointment_id = response.get('records')[0].get('id')
289

  
290
            # add a message in formdata.evolution
291
            if formdata.evolution:
292
                evo = formdata.evolution[-1]
293
            else:
294
                formdata.evolution = []
295
                evo = Evolution()
296
                evo.time = time.localtime()
297
                evo.status = formdata.status
298
                formdata.evolution.append(evo)
299
            evo.add_part(AppointmentPart(response.get('records')[0]))
300

  
301
        formdata.store()
302

  
303
        if (success and self.status_on_success) or (success is False and self.status_on_failure):
304
            if success:
305
                formdata.status = 'wf-%s' % self.status_on_success
306
            else:
307
                formdata.status = 'wf-%s' % self.status_on_failure
308

  
309
register_item_class(ClicRdvCreateAppointment)
310

  
311

  
312
class ClicRdvCancelAppointment(WorkflowStatusItem):
313
    description = N_('Cancel a ClicRDV Appointment')
314
    key = 'clicrdv-cancel'
315
    category = 'interaction'
316

  
317
    endpoint = False
318

  
319
    status_on_success = None
320
    status_on_failure = None
321

  
322
    def get_parameters(self):
323
        return ('status_on_success', 'status_on_failure')
324

  
325
    def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
326
        if 'status_on_success' in parameters:
327
            form.add(SingleSelectWidget, '%sstatus_on_success' % prefix,
328
                    title=_('Status On Success'), value=self.status_on_success,
329
                    options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status])
330
        if 'status_on_failure' in parameters:
331
            form.add(SingleSelectWidget, '%sstatus_on_failure' % prefix,
332
                    title=_('Status On Failure'), value=self.status_on_failure,
333
                    options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status])
334

  
335
    def is_available(self, workflow=None):
336
        return get_publisher().has_site_option('clicrdv')
337
    is_available = classmethod(is_available)
338

  
339
    def render_as_line(self):
340
        return _('Cancel an appointment in ClicRDV')
341

  
342
    def perform(self, formdata):
343
        success = True
344

  
345
        for evo in [evo for evo in formdata.evolution if evo.parts]:
346
            for part in [part for part in evo.parts if isinstance(part, AppointmentPart)]:
347
                appointment_id = part.id
348
                try:
349
                    req = get_clicrdv_req('appointments/%s' % appointment_id)
350
                    req.get_method = (lambda: 'DELETE')
351
                    fd = urllib2.urlopen(req)
352
                    none = fd.read()
353
                except urllib2.URLError:
354
                    # clicrdv will return a "Bad Request" (HTTP 400) response
355
                    # when it's not possible to remove an appointment
356
                    # (for example because it's too late)
357
                    success = False
358

  
359
        if (success and self.status_on_success) or (success is False and self.status_on_failure):
360
            if success:
361
                formdata.status = 'wf-%s' % self.status_on_success
362
            else:
363
                formdata.status = 'wf-%s' % self.status_on_failure
364

  
365
register_item_class(ClicRdvCancelAppointment)
auquotidien/modules/connectors.py
1
from . import clicrdv
2 1
from . import abelium_domino_workflow
3
-