This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
auquotidien/auquotidien/modules/clicrdv.py

366 lines
14 KiB
Python

import base64
import datetime
import urllib2
try:
import json
except ImportError:
import simplejson as json
import time
import vobject
from wcs.qommon import _
from wcs.qommon import get_cfg
from wcs.qommon.misc import format_time
from wcs.qommon.form import *
from wcs.data_sources import register_data_source_function
from wcs.formdata import Evolution
from wcs.forms.common import FormStatusPage
from wcs.workflows import Workflow, WorkflowStatusItem, register_item_class
def get_clicrdv_req(url):
misc_cfg = get_cfg('misc', {})
url = 'https://%s/api/v1/%s' % (
misc_cfg.get('aq-clicrdv-server', 'sandbox.clicrdv.com'), url)
if '?' in url:
url = url + '&apikey=%s&format=json' % misc_cfg.get('aq-clicrdv-api-key')
else:
url = url + '?apikey=%s&format=json' % misc_cfg.get('aq-clicrdv-api-key')
req = urllib2.Request(url)
username = misc_cfg.get('aq-clicrdv-api-username')
password = misc_cfg.get('aq-clicrdv-api-password')
authheader = 'Basic ' + base64.encodestring('%s:%s' % (username, password))[:-1]
req.add_header('Authorization', authheader)
return req
def get_json(url):
return json.load(urllib2.urlopen(get_clicrdv_req(url)))
def as_str(s):
if type(s) is unicode:
return s.encode(get_publisher().site_charset)
return s
def get_all_intervention_sets():
interventions_set = []
for interventionset in sorted(get_json('interventionsets').get('records'),
lambda x,y: cmp(x['sort'],y['sort'])):
interventions = []
for intervention in sorted(get_json('interventionsets/%s/interventions' % interventionset.get('id')).get('records'),
lambda x,y: cmp(x['sort'], y['sort'])):
if intervention.get('deleted') == True:
continue
name = '%s' % as_str(intervention.get('publicname'))
if not name:
name = '%s' % as_str(intervention.get('name'))
interventions.append((intervention.get('id'), as_str(name)))
interventions_set.append({
'id': interventionset.get('id'),
'group_id': interventionset.get('group_id'),
'name': as_str(interventionset.get('name')),
'publicname': as_str(interventionset.get('publicname')) or '',
'description': as_str(interventionset.get('description')) or '',
'interventions': interventions
})
return interventions_set
def get_all_interventions():
interventions = []
for s in get_all_intervention_sets():
for i, publicname in s['interventions']:
intervention_label = '%s - %s' % (s['publicname'], publicname)
interventions.append((i, as_str(intervention_label)))
return interventions
def get_interventions_in_set(interventionset_id):
interventions = []
interventions_json = get_json('interventionsets/%s/interventions' % interventionset_id)
for intervention in interventions_json.get('records'):
if intervention.get('deleted') != True:
name = '%s' % as_str(intervention.get('publicname'))
if not name:
name = '%s' % as_str(intervention.get('name'))
interventions.append((intervention.get('id'), name))
return interventions
def get_available_timeslots(intervention, date_start=None, date_end=None):
timeslots = []
iid = intervention
gid = get_json('interventions/%s' % iid).get('group_id')
request_url = 'availabletimeslots?intervention_ids[]=%s&group_id=%s' % (iid, gid)
if date_start is None:
date_start = datetime.datetime.today().strftime('%Y-%m-%d')
if date_end is None:
date_end = (datetime.datetime.today() + datetime.timedelta(366)).strftime('%Y-%m-%d')
if date_start:
request_url = request_url + '&start=%s' % urllib2.quote(date_start)
if date_end:
request_url = request_url + '&end=%s' % urllib2.quote(date_end)
for timeslot in get_json(request_url).get('availabletimeslots'):
timeslots.append(timeslot.get('start'))
timeslots.sort()
return timeslots
def get_available_dates(intervention):
dates = []
for timeslot in get_available_timeslots(intervention):
parsed = time.strptime(timeslot, '%Y-%m-%d %H:%M:%S')
date_tuple = (time.strftime('%Y-%m-%d', parsed),
format_time(parsed, '%(weekday_name)s %(day)0.2d/%(month)0.2d/%(year)s'))
if date_tuple in dates:
continue
dates.append(date_tuple)
return dates
def get_available_times(intervention, date):
times = []
timeslots = get_available_timeslots(intervention,
date_start='%s 00:00:00' % date,
date_end='%s 23:59:59' % date)
for timeslot in timeslots:
parsed = time.strptime(timeslot, '%Y-%m-%d %H:%M:%S')
time_tuple = (time.strftime('%H:%M:%S', parsed),
time.strftime('%Hh%M', parsed))
times.append(time_tuple)
times.sort()
return times
register_data_source_function(get_all_interventions, 'clicrdv_get_all_interventions')
register_data_source_function(get_interventions_in_set, 'clicrdv_get_interventions_in_set')
register_data_source_function(get_available_dates, 'clicrdv_get_available_dates')
register_data_source_function(get_available_times, 'clicrdv_get_available_times')
def form_download_event(self):
self.check_receiver()
found = False
for evo in self.filled.evolution:
if evo.parts:
for p in evo.parts:
if not isinstance(p, AppointmentPart):
continue
cal = vobject.iCalendar()
cal.add('prodid').value = '-//Entr\'ouvert//NON SGML Publik'
vevent = vobject.newFromBehavior('vevent')
vevent.add('uid').value = 'clicrdv-%s' % p.id
vevent.add('summary').value = p.json_dict.get('group_name')
vevent.add('dtstart').value = datetime.datetime.strptime(
p.json_dict.get('start'), '%Y-%m-%d %H:%M:%S')
vevent.add('dtend').value = datetime.datetime.strptime(
p.json_dict.get('end'), '%Y-%m-%d %H:%M:%S')
vevent.add('location').value = p.json_dict.get('location')
cal.add(vevent)
response = get_response()
response.set_content_type('text/calendar')
return cal.serialize()
raise TraversalError()
class AppointmentPart(object):
def __init__(self, json_dict):
self.id = json_dict.get('id')
self.json_dict = json_dict
def view(self):
return htmltext('<p class="appointment"><a href="clicrdvevent">%s</a></p>' % (
_('Download Appointment')))
class AppointmentErrorPart(object):
def __init__(self, msg):
self.msg = msg
def view(self):
return htmltext('<p class="appointment-error">%s</p>' % str(self.msg))
class ClicRdvCreateAppointment(WorkflowStatusItem):
description = N_('Create a ClicRDV Appointment')
key = 'clicrdv-create'
category = 'interaction'
endpoint = False
var_firstname = None
var_lastname = None
var_email = None
var_firstphone = None
var_secondphone = None
var_datetime = None
var_intervention_id = None
status_on_success = None
status_on_failure = None
def init(cls):
FormStatusPage._q_extra_exports.append('clicrdvevent')
FormStatusPage.clicrdvevent = form_download_event
init = classmethod(init)
def is_available(self, workflow=None):
return get_publisher().has_site_option('clicrdv')
is_available = classmethod(is_available)
def render_as_line(self):
return _('Create an appointment in ClicRDV')
def get_parameters(self):
return ('var_firstname', 'var_lastname', 'var_email', 'var_firstphone',
'var_secondphone', 'var_datetime', 'var_intervention_id',
'status_on_success', 'status_on_failure')
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
parameter_labels = {
'var_firstname': N_('First Name'),
'var_lastname': N_('Last Name'),
'var_email': N_('Email'),
'var_firstphone': N_('Phone (1st)'),
'var_secondphone': N_('Phone (2nd)'),
'var_datetime': N_('Date/time'),
'var_intervention_id': N_('Intervention Id'),
}
for parameter in self.get_parameters():
if not parameter in parameter_labels:
continue
if parameter in parameters:
form.add(StringWidget, '%s%s' % (prefix, parameter),
title=_(parameter_labels.get(parameter)),
value=getattr(self, parameter),
required=False)
if 'status_on_success' in parameters:
form.add(SingleSelectWidget, '%sstatus_on_success' % prefix,
title=_('Status On Success'), value=self.status_on_success,
options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status])
if 'status_on_failure' in parameters:
form.add(SingleSelectWidget, '%sstatus_on_failure' % prefix,
title=_('Status On Failure'), value=self.status_on_failure,
options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status])
def perform(self, formdata):
args = {}
for parameter in self.get_parameters():
args[parameter] = self.compute(getattr(self, parameter))
if not args.get(parameter):
del args[parameter]
message = {'appointment':
{'fiche': {'firstname': args.get('var_firstname', '-'),
'lastname': args.get('var_lastname', '-'),
'email': args.get('var_email'),
'firstphone': args.get('var_firstphone'),
'secondphone': args.get('var_secondphone'),
},
'date': args.get('var_datetime'),
'intervention_ids': [int(args.get('var_intervention_id'))],
# 'comments': '-',
'websource': 'Publik'}
}
req = get_clicrdv_req('appointments')
req.add_data(json.dumps(message))
req.add_header('Content-Type', 'application/json')
try:
fd = urllib2.urlopen(req)
except urllib2.HTTPError, e:
success = False
try:
msg = json.load(e.fp)[0].get('error')
except:
msg = _('unknown error')
if formdata.evolution:
evo = formdata.evolution[-1]
else:
formdata.evolution = []
evo = Evolution()
evo.time = time.localtime()
evo.status = formdata.status
formdata.evolution.append(evo)
evo.add_part(AppointmentErrorPart(msg))
else:
success = True
response = json.load(fd)
appointment_id = response.get('records')[0].get('id')
# add a message in formdata.evolution
if formdata.evolution:
evo = formdata.evolution[-1]
else:
formdata.evolution = []
evo = Evolution()
evo.time = time.localtime()
evo.status = formdata.status
formdata.evolution.append(evo)
evo.add_part(AppointmentPart(response.get('records')[0]))
formdata.store()
if (success and self.status_on_success) or (success is False and self.status_on_failure):
if success:
formdata.status = 'wf-%s' % self.status_on_success
else:
formdata.status = 'wf-%s' % self.status_on_failure
register_item_class(ClicRdvCreateAppointment)
class ClicRdvCancelAppointment(WorkflowStatusItem):
description = N_('Cancel a ClicRDV Appointment')
key = 'clicrdv-cancel'
category = 'interaction'
endpoint = False
status_on_success = None
status_on_failure = None
def get_parameters(self):
return ('status_on_success', 'status_on_failure')
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None):
if 'status_on_success' in parameters:
form.add(SingleSelectWidget, '%sstatus_on_success' % prefix,
title=_('Status On Success'), value=self.status_on_success,
options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status])
if 'status_on_failure' in parameters:
form.add(SingleSelectWidget, '%sstatus_on_failure' % prefix,
title=_('Status On Failure'), value=self.status_on_failure,
options = [(None, '---')] + [(x.id, x.name) for x in self.parent.parent.possible_status])
def is_available(self, workflow=None):
return get_publisher().has_site_option('clicrdv')
is_available = classmethod(is_available)
def render_as_line(self):
return _('Cancel an appointment in ClicRDV')
def perform(self, formdata):
success = True
for evo in [evo for evo in formdata.evolution if evo.parts]:
for part in [part for part in evo.parts if isinstance(part, AppointmentPart)]:
appointment_id = part.id
try:
req = get_clicrdv_req('appointments/%s' % appointment_id)
req.get_method = (lambda: 'DELETE')
fd = urllib2.urlopen(req)
none = fd.read()
except urllib2.URLError:
# clicrdv will return a "Bad Request" (HTTP 400) response
# when it's not possible to remove an appointment
# (for example because it's too late)
success = False
if (success and self.status_on_success) or (success is False and self.status_on_failure):
if success:
formdata.status = 'wf-%s' % self.status_on_success
else:
formdata.status = 'wf-%s' % self.status_on_failure
register_item_class(ClicRdvCancelAppointment)