Projet

Général

Profil

Télécharger (14,2 ko) Statistiques
| Branche: | Tag: | Révision:

root / extra / modules / clicrdv.py @ 8881779a

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 qommon import get_cfg
14
from qommon.misc import format_time
15
from qommon.form import *
16

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
161
    raise TraversalError()
162

    
163

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

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

    
173

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

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

    
181

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

    
187
    endpoint = False
188

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

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

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

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

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

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

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

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

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

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

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

    
300
        formdata.store()
301

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

    
308
register_item_class(ClicRdvCreateAppointment)
309

    
310

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

    
316
    endpoint = False
317

    
318
    status_on_success = None
319
    status_on_failure = None
320

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

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

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

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

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

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

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

    
364
register_item_class(ClicRdvCancelAppointment)
(13-13/27)