class RemindersListView(BaseMessagingSectionView): template_name = 'reminders/reminders_list.html' urlname = "list_reminders_new" page_title = ugettext_noop("Reminder Definitions") @method_decorator(requires_old_reminder_framework()) @method_decorator(requires_privilege_with_fallback(privileges.OUTBOUND_SMS) ) @use_datatables def dispatch(self, request, *args, **kwargs): return super(BaseMessagingSectionView, self).dispatch(request, *args, **kwargs) @property def page_url(self): return reverse(self.urlname, args=[self.domain]) @property def can_use_survey(self): return can_use_survey_reminders(self.request) @property def reminders(self): all_handlers = CaseReminderHandler.get_handlers( self.domain, reminder_type_filter=REMINDER_TYPE_DEFAULT) if not self.can_use_survey: all_handlers = [ x for x in all_handlers if x.method not in [METHOD_IVR_SURVEY, METHOD_SMS_SURVEY] ] for handler in all_handlers: yield self._fmt_reminder_data(handler) @property def page_context(self): return { 'reminders': list(self.reminders), 'reminders_migration_in_progress': (self.reminders_migration_in_progress and not self.new_reminders_migrator), } @property def reminder_id(self): return self.request.POST['reminderId'] @property @memoized def reminder(self): return CaseReminderHandler.get(self.reminder_id) def _fmt_reminder_data(self, reminder): return { 'id': reminder._id, 'isActive': reminder.active, 'caseType': reminder.case_type, 'name': reminder.nickname, 'url': reverse(EditScheduledReminderView.urlname, args=[self.domain, reminder._id]), } def get_action_response(self, action): try: assert self.reminder.domain == self.domain assert self.reminder.doc_type == "CaseReminderHandler" if self.reminder.locked: return { 'success': False, 'locked': True, } if action in [ACTION_ACTIVATE, ACTION_DEACTIVATE]: self.reminder.active = (action == ACTION_ACTIVATE) self.reminder.save() elif action == ACTION_DELETE: self.reminder.retire() return { 'success': True, } except Exception as e: msg = ("Couldn't process action '%s' for reminder definition" % action) notify_exception(None, message=msg, details={ 'domain': self.domain, 'handler_id': self.reminder_id, }) return { 'success': False, } def get(self, *args, **kwargs): if self.reminders_migration_in_progress: add_migration_in_progress_message(self.request) return super(RemindersListView, self).get(*args, **kwargs) def post(self, *args, **kwargs): action = self.request.POST.get('action') if action in [ACTION_ACTIVATE, ACTION_DEACTIVATE, ACTION_DELETE]: if self.reminders_migration_in_progress and not self.new_reminders_migrator: return HttpResponse( "Cannot complete action because reminders migration is in progress.", status=400) return HttpResponse(json.dumps(self.get_action_response(action))) return HttpResponse(status=400)
class ScheduledRemindersCalendarView(BaseMessagingSectionView): urlname = 'scheduled_reminders' page_title = ugettext_noop("Reminder Calendar") template_name = 'reminders/partial/scheduled_reminders.html' @method_decorator(requires_old_reminder_framework()) @method_decorator(requires_privilege_with_fallback(privileges.OUTBOUND_SMS) ) @method_decorator(reminders_framework_permission) def dispatch(self, *args, **kwargs): return super(BaseMessagingSectionView, self).dispatch(*args, **kwargs) @property def page_context(self): page_context = super(ScheduledRemindersCalendarView, self).page_context timezone = Domain.get_by_name(self.domain).get_default_timezone() reminders = CaseReminderHandler.get_all_reminders(self.domain) dates = [] now = datetime.utcnow() timezone_now = datetime.now(timezone) today = timezone_now.date() def adjust_next_fire_to_timezone(reminder_utc): return ServerTime( reminder_utc.next_fire).user_time(timezone).done().replace( tzinfo=None) if reminders: start_date = adjust_next_fire_to_timezone(reminders[0]).date() if today < start_date: start_date = today end_date = adjust_next_fire_to_timezone(reminders[-1]).date() else: start_date = end_date = today # make sure start date is a Monday and enddate is a Sunday start_date -= timedelta(days=start_date.weekday()) end_date += timedelta(days=6 - end_date.weekday()) while start_date <= end_date: dates.append(start_date) start_date += timedelta(days=1) reminder_data = [] for reminder in reminders: handler = reminder.handler recipient = reminder.recipient recipient_desc = get_recipient_name(recipient) case = reminder.case reminder_data.append({ "handler_name": handler.nickname, "next_fire": adjust_next_fire_to_timezone(reminder), "recipient_desc": recipient_desc, "recipient_type": handler.recipient, "case_id": case.case_id if case is not None else None, "case_name": case.name if case is not None else None, }) page_context.update({ 'domain': self.domain, 'reminder_data': reminder_data, 'dates': dates, 'today': today, 'now': now, 'timezone': timezone, 'timezone_now': timezone_now, }) return page_context
class CreateScheduledReminderView(BaseMessagingSectionView): urlname = 'create_reminder_schedule' page_title = ugettext_noop("Schedule Reminder") template_name = 'reminders/manage_scheduled_reminder.html' ui_type = UI_SIMPLE_FIXED @method_decorator(requires_old_reminder_framework()) @method_decorator(reminders_framework_permission) @use_jquery_ui @use_timepicker @use_select2 def dispatch(self, request, *args, **kwargs): if self.reminders_migration_in_progress and not self.new_reminders_migrator: return HttpResponseRedirect( reverse(RemindersListView.urlname, args=[self.domain])) return super(CreateScheduledReminderView, self).dispatch(request, *args, **kwargs) @property def reminder_form_class(self): return { UI_COMPLEX: ComplexScheduleCaseReminderForm, UI_SIMPLE_FIXED: SimpleScheduleCaseReminderForm, }[self.ui_type] @property @memoized def schedule_form(self): if self.request.method == 'POST': return self.reminder_form_class( self.request.POST, domain=self.domain, is_previewer=self.is_previewer, can_use_survey=can_use_survey_reminders(self.request), available_languages=self.available_languages, ) return self.reminder_form_class( is_previewer=self.is_previewer, domain=self.domain, can_use_survey=can_use_survey_reminders(self.request), available_languages=self.available_languages, ) @property def available_languages(self): """ Returns a the list of language codes available for the domain, or [] if no languages are specified. """ translation_doc = StandaloneTranslationDoc.get_obj(self.domain, "sms") if translation_doc and translation_doc.langs: return translation_doc.langs return [] @property def is_previewer(self): return self.request.couch_user.is_previewer() @property def parent_pages(self): return [ { 'title': _("Reminders"), 'url': reverse(RemindersListView.urlname, args=[self.domain]), }, ] @property def page_context(self): return { 'form': self.schedule_form, 'event_form': CaseReminderEventForm(ui_type=self.ui_type), 'message_form': CaseReminderEventMessageForm(), 'ui_type': self.ui_type, 'available_languages': self.available_languages, } @property def available_case_types(self): case_types = [] for app in self.apps: case_types.extend([m.case_type for m in app.modules]) return set(case_types) @property def action(self): return self.request.POST.get('action') @property def case_type(self): return self.request.POST.get('caseType') @property @memoized def apps(self): return get_apps_in_domain(self.domain, include_remote=False) @property def search_term(self): return self.request.POST.get('term') @property def search_case_type_response(self): return list(self.available_case_types) def clean_dict_list(self, dict_list): """ Takes a dict of {string: list} and returns the same result, only removing any duplicate entries in each of the lists. """ result = {} for key in dict_list: result[key] = list(set(dict_list[key])) return result @property def search_form_by_id_response(self): """ Returns a dict of {"id": [form unique id], "text": [full form path]} """ form_unique_id = self.search_term try: form = Form.get_form(form_unique_id) assert form.get_app().domain == self.domain return { 'text': form.full_path_name, 'id': form_unique_id, } except: return {} @property def search_case_property_response(self): """ Returns a dict of {case type: [case properties...]} """ result = {} for app in self.apps: case_types = list(set([m.case_type for m in app.modules])) for case_type in case_types: if case_type not in result: result[case_type] = ['name'] for properties in get_case_properties(app, [case_type]).values(): result[case_type].extend(properties) return self.clean_dict_list(result) def get_parent_child_types(self): """ Returns a dict of {parent case type: [subcase types...]} """ parent_child_types = {} for app in self.apps: for module in app.get_modules(): case_type = module.case_type if case_type not in parent_child_types: parent_child_types[case_type] = [] if module.module_type == 'basic': for form in module.get_forms(): for subcase in form.actions.subcases: parent_child_types[case_type].append( subcase.case_type) elif module.module_type == 'advanced': for form in module.get_forms(): for subcase in form.actions.get_open_subcase_actions( case_type): parent_child_types[case_type].append( subcase.case_type) return self.clean_dict_list(parent_child_types) @property def search_subcase_property_response(self): """ Returns a dict of {parent case type: [subcase properties]} """ result = {} parent_child_types = self.get_parent_child_types() all_case_properties = self.search_case_property_response for parent_type in parent_child_types: result[parent_type] = [] for subcase_type in parent_child_types[parent_type]: result[parent_type].extend(all_case_properties[subcase_type]) return self.clean_dict_list(result) @property def search_forms_response(self): forms = [] for app in self.apps: for module in app.get_modules(): for form in module.get_forms(): forms.append({ 'text': form.full_path_name, 'id': form.unique_id, }) if not self.search_term: return forms final_forms = [] search_terms = self.search_term.split(" ") for form in forms: matches = [t for t in search_terms if t in form['text']] if len(matches) == len(search_terms): final_forms.append(form) return final_forms def _filter_by_term(self, filter_list): return [f for f in filter_list if self.search_term in f] def _format_response(self, resp_list): return [{'text': r, 'id': r} for r in resp_list] def post(self, *args, **kwargs): if self.action in [ 'search_case_type', 'search_case_property', 'search_subcase_property', 'search_forms', 'search_form_by_id', ]: return HttpResponse( json.dumps(getattr(self, '%s_response' % self.action))) if self.schedule_form.is_valid(): self.process_schedule_form() return HttpResponseRedirect( reverse(RemindersListView.urlname, args=[self.domain])) else: messages.error(self.request, "There were errors saving your reminder.") return self.get(*args, **kwargs) def process_schedule_form(self): new_handler = CaseReminderHandler( use_today_if_start_date_is_blank=False) self.schedule_form.save(new_handler)
class CreateBroadcastView(BaseMessagingSectionView): urlname = 'add_broadcast' page_title = ugettext_lazy('New Broadcast') template_name = 'reminders/broadcast.html' force_create_new_broadcast = False @method_decorator(requires_old_reminder_framework()) @method_decorator(requires_privilege_with_fallback(privileges.OUTBOUND_SMS) ) @use_jquery_ui @use_timepicker @use_select2 def dispatch(self, *args, **kwargs): if self.reminders_migration_in_progress and not self.new_reminders_migrator: return HttpResponseRedirect( reverse(BroadcastListView.urlname, args=[self.domain])) return super(BaseMessagingSectionView, self).dispatch(*args, **kwargs) @property @memoized def project_timezone(self): return get_timezone_for_user(None, self.domain) @property def parent_pages(self): return [ { 'title': BroadcastListView.page_title, 'url': reverse(BroadcastListView.urlname, args=[self.domain]), }, ] def create_new_broadcast(self): return CaseReminderHandler( domain=self.domain, nickname='One-time Reminder', reminder_type=REMINDER_TYPE_ONE_TIME, ) @property @memoized def broadcast(self): return self.create_new_broadcast() @property def form_kwargs(self): return { 'domain': self.domain, 'can_use_survey': can_use_survey_reminders(self.request), } @property def form_class(self): if toggles.EWS_BROADCAST_BY_ROLE.enabled(self.domain): return EWSBroadcastForm else: return BroadcastForm @property @memoized def broadcast_form(self): if self.request.method == 'POST': return self.form_class(self.request.POST, **self.form_kwargs) else: return self.form_class(**self.form_kwargs) @property def page_context(self): return { 'form': self.broadcast_form, } def save_model(self, broadcast, form): broadcast.default_lang = 'xx' broadcast.method = form.cleaned_data.get('content_type') broadcast.recipient = form.cleaned_data.get('recipient_type') broadcast.start_condition_type = ON_DATETIME broadcast.start_datetime = form.cleaned_data.get('datetime') broadcast.start_offset = 0 broadcast.events = [ CaseReminderEvent( day_num=0, fire_time=time(0, 0), form_unique_id=form.cleaned_data.get('form_unique_id'), message=({ broadcast.default_lang: form.cleaned_data.get('message') } if form.cleaned_data.get('message') else {}), subject=({ broadcast.default_lang: form.cleaned_data.get('subject') } if form.cleaned_data.get('subject') else {}), callback_timeout_intervals=[], ) ] broadcast.schedule_length = 1 broadcast.event_interpretation = EVENT_AS_OFFSET broadcast.max_iteration_count = 1 broadcast.sample_id = form.cleaned_data.get('case_group_id') broadcast.user_group_id = form.cleaned_data.get('user_group_id') broadcast.location_ids = form.cleaned_data.get('location_ids') broadcast.include_child_locations = form.cleaned_data.get( 'include_child_locations') if toggles.EWS_BROADCAST_BY_ROLE.enabled(self.domain): broadcast.user_data_filter = form.get_user_data_filter() broadcast.save() def post(self, request, *args, **kwargs): if self.broadcast_form.is_valid(): if self.force_create_new_broadcast: broadcast = self.create_new_broadcast() else: broadcast = self.broadcast self.save_model(broadcast, self.broadcast_form) return HttpResponseRedirect( reverse(BroadcastListView.urlname, args=[self.domain])) return self.get(request, *args, **kwargs)
class BroadcastListView(BaseMessagingSectionView, DataTablesAJAXPaginationMixin): template_name = 'reminders/broadcasts_list.html' urlname = 'list_broadcasts' page_title = ugettext_lazy('Broadcasts') LIST_UPCOMING = 'list_upcoming' LIST_PAST = 'list_past' DELETE_BROADCAST = 'delete_broadcast' @method_decorator(requires_old_reminder_framework()) @method_decorator(requires_privilege_with_fallback(privileges.OUTBOUND_SMS) ) @use_datatables def dispatch(self, request, *args, **kwargs): return super(BroadcastListView, self).dispatch(request, *args, **kwargs) @property def page_context(self): return { 'reminders_migration_in_progress': (self.reminders_migration_in_progress and not self.new_reminders_migrator), } @property @memoized def project_timezone(self): return get_timezone_for_user(None, self.domain) def format_recipients(self, broadcast): reminders = broadcast.get_reminders() if len(reminders) == 0: return _('(none)') return get_recipient_name(reminders[0].recipient, include_desc=False) def format_content(self, broadcast): if broadcast.method == METHOD_SMS_SURVEY: content = get_form_name(broadcast.events[0].form_unique_id) else: message = broadcast.events[0].message[broadcast.default_lang] if len(message) > 50: content = '"%s..."' % message[:47] else: content = '"%s"' % message return content def format_broadcast_name(self, broadcast): user_time = ServerTime(broadcast.start_datetime).user_time( self.project_timezone) return user_time.ui_string(SERVER_DATETIME_FORMAT) def format_broadcast_data(self, ids): broadcasts = CaseReminderHandler.get_handlers_from_ids(ids) result = [] for broadcast in broadcasts: display = self.format_broadcast_name(broadcast) result.append([ display, self.format_recipients(broadcast), self.format_content(broadcast), broadcast._id, reverse(EditBroadcastView.urlname, args=[self.domain, broadcast._id]), reverse(CopyBroadcastView.urlname, args=[self.domain, broadcast._id]), ]) return result def get_broadcast_ajax_response(self, upcoming=True): """ upcoming - True to include only upcoming broadcasts, False to include only past broadcasts. """ if upcoming: ids = CaseReminderHandler.get_upcoming_broadcast_ids(self.domain) else: ids = CaseReminderHandler.get_past_broadcast_ids(self.domain) total_records = len(ids) ids = ids[self.display_start:self.display_start + self.display_length] data = self.format_broadcast_data(ids) return self.datatables_ajax_response(data, total_records) def delete_broadcast(self, broadcast_id): try: broadcast = CaseReminderHandler.get(broadcast_id) except: raise Http404() if broadcast.doc_type != 'CaseReminderHandler' or broadcast.domain != self.domain: raise Http404() broadcast.retire() return HttpResponse() def get(self, *args, **kwargs): action = self.request.GET.get('action') if action in (self.LIST_UPCOMING, self.LIST_PAST): upcoming = (action == self.LIST_UPCOMING) return self.get_broadcast_ajax_response(upcoming) else: if self.reminders_migration_in_progress: add_migration_in_progress_message(self.request) return super(BroadcastListView, self).get(*args, **kwargs) def post(self, *args, **kwargs): action = self.request.POST.get('action') if action == self.DELETE_BROADCAST: if self.reminders_migration_in_progress and not self.new_reminders_migrator: return HttpResponse( "Cannot complete action because reminders migration is in progress.", status=400) return self.delete_broadcast( self.request.POST.get('broadcast_id', None)) else: return HttpResponse(status=400)