class AddStructuredKeywordView(BaseMessagingSectionView): urlname = 'add_structured_keyword' page_title = ugettext_noop("New Structured Keyword") template_name = 'reminders/keyword.html' process_structured_message = True @method_decorator(requires_privilege_with_fallback(privileges.INBOUND_SMS)) def dispatch(self, *args, **kwargs): return super(AddStructuredKeywordView, self).dispatch(*args, **kwargs) @property def parent_pages(self): return [ { 'title': KeywordsListView.page_title, 'url': reverse(KeywordsListView.urlname, args=[self.domain]), }, ] @property @memoized def keyword(self): return Keyword(domain=self.domain) @property def keyword_form(self): raise NotImplementedError("you must implement keyword_form") @property def page_context(self): return { 'form': self.keyword_form, 'form_list': get_form_list(self.domain), } @property @memoized def keyword_form(self): if self.request.method == 'POST': return KeywordForm( self.request.POST, domain=self.domain, process_structured=self.process_structured_message, ) return KeywordForm( domain=self.domain, process_structured=self.process_structured_message, ) def post(self, request, *args, **kwargs): if self.keyword_form.is_valid(): with transaction.atomic(): self.keyword.keyword = self.keyword_form.cleaned_data['keyword'] self.keyword.description = self.keyword_form.cleaned_data['description'] self.keyword.delimiter = self.keyword_form.cleaned_data['delimiter'] self.keyword.override_open_sessions = self.keyword_form.cleaned_data['override_open_sessions'] self.keyword.initiator_doc_type_filter = [] if self.keyword_form.cleaned_data['allow_keyword_use_by'] == 'users': self.keyword.initiator_doc_type_filter.append('CommCareUser') if self.keyword_form.cleaned_data['allow_keyword_use_by'] == 'cases': self.keyword.initiator_doc_type_filter.append('CommCareCase') self.keyword.save() self.keyword.keywordaction_set.all().delete() if self.keyword_form.cleaned_data['sender_content_type'] != NO_RESPONSE: self.keyword.keywordaction_set.create( recipient=KeywordAction.RECIPIENT_SENDER, action=self.keyword_form.cleaned_data['sender_content_type'], message_content=self.keyword_form.cleaned_data['sender_message'], form_unique_id=self.keyword_form.cleaned_data['sender_form_unique_id'], ) if self.process_structured_message: self.keyword.keywordaction_set.create( recipient=KeywordAction.RECIPIENT_SENDER, action=KeywordAction.ACTION_STRUCTURED_SMS, form_unique_id=self.keyword_form.cleaned_data['structured_sms_form_unique_id'], use_named_args=self.keyword_form.cleaned_data['use_named_args'], named_args=self.keyword_form.cleaned_data['named_args'], named_args_separator=self.keyword_form.cleaned_data['named_args_separator'], ) if self.keyword_form.cleaned_data['other_recipient_content_type'] != NO_RESPONSE: self.keyword.keywordaction_set.create( recipient=self.keyword_form.cleaned_data['other_recipient_type'], recipient_id=self.keyword_form.cleaned_data['other_recipient_id'], action=self.keyword_form.cleaned_data['other_recipient_content_type'], message_content=self.keyword_form.cleaned_data['other_recipient_message'], form_unique_id=self.keyword_form.cleaned_data['other_recipient_form_unique_id'], ) return HttpResponseRedirect(reverse(KeywordsListView.urlname, args=[self.domain])) return self.get(request, *args, **kwargs)
class CreateConditionalAlertView(BaseMessagingSectionView, AsyncHandlerMixin): urlname = 'create_conditional_alert' page_title = ugettext_lazy('New Conditional Alert') template_name = 'scheduling/conditional_alert.html' async_handlers = [ConditionalAlertAsyncHandler] read_only_mode = False @property def help_text(self): return _(""" For information on Conditional Alerts, see the <a target="_blank" href="https://confluence.dimagi.com/display/commcarepublic/Conditional+Alerts"> Conditional Alerts </a> help page. """) @method_decorator( requires_privilege_with_fallback(privileges.REMINDERS_FRAMEWORK)) @use_jquery_ui @use_timepicker def dispatch(self, *args, **kwargs): return super(CreateConditionalAlertView, self).dispatch(*args, **kwargs) @property def parent_pages(self): return [ { 'title': ConditionalAlertListView.page_title, 'url': reverse(ConditionalAlertListView.urlname, args=[self.domain]), }, ] @property def page_context(self): context = super().page_context context.update({ 'basic_info_form': self.basic_info_form, 'criteria_form': self.criteria_form, 'help_text': self.help_text, 'schedule_form': self.schedule_form, 'read_only_mode': self.read_only_mode, 'is_system_admin': self.is_system_admin, 'criteria_form_active': False, 'schedule_form_active': False, 'new_rule': not bool(self.rule), 'rule_name': self.rule.name if self.rule else '', }) if self.request.method == 'POST': context.update({ 'criteria_form_active': not self.criteria_form.is_valid() or self.schedule_form.is_valid(), 'schedule_form_active': not self.schedule_form.is_valid() and self.criteria_form.is_valid(), 'rule_name': self.basic_info_form.rule_name, }) return context @cached_property def schedule_form(self): args = [ self.domain, self.schedule, self.can_use_inbound_sms, self.rule, self.criteria_form, ] if self.request.method == 'POST': args.append(self.request.POST) return ConditionalAlertScheduleForm( *args, is_system_admin=self.is_system_admin) @property def schedule(self): return None @property def rule(self): return None @cached_property def basic_info_form(self): if self.request.method == 'POST': return ConditionalAlertForm(self.domain, self.rule, self.request.POST) return ConditionalAlertForm(self.domain, self.rule) @cached_property def criteria_form(self): kwargs = { 'rule': self.rule, 'is_system_admin': self.is_system_admin, } if self.request.method == 'POST': return ConditionalAlertCriteriaForm(self.domain, self.request.POST, **kwargs) return ConditionalAlertCriteriaForm(self.domain, **kwargs) def post(self, request, *args, **kwargs): if self.async_response is not None: return self.async_response basic_info_form_valid = self.basic_info_form.is_valid() criteria_form_valid = self.criteria_form.is_valid() schedule_form_valid = self.schedule_form.is_valid() if self.read_only_mode: # Don't allow making changes to rules that have custom # criteria/actions unless the user has permission to return HttpResponseBadRequest() if basic_info_form_valid and criteria_form_valid and schedule_form_valid: if not self.is_system_admin and ( self.criteria_form.requires_system_admin_to_save or self.schedule_form.requires_system_admin_to_save): # Don't allow adding custom criteria/actions to rules # unless the user has permission to return HttpResponseBadRequest() if not self.can_use_inbound_sms and self.schedule_form.uses_sms_survey: return HttpResponseBadRequest( "Cannot create or edit survey reminders because subscription " "does not have access to inbound SMS") with transaction.atomic(): if self.rule: rule = self.rule else: rule = AutomaticUpdateRule( domain=self.domain, active=True, workflow=AutomaticUpdateRule.WORKFLOW_SCHEDULING, ) rule.name = self.basic_info_form.cleaned_data['name'] self.criteria_form.save_criteria(rule) self.schedule_form.save_rule_action_and_schedule(rule) initiate_messaging_rule_run(rule) return HttpResponseRedirect( reverse(ConditionalAlertListView.urlname, args=[self.domain])) return self.get(request, *args, **kwargs)
class BulkSMSVerificationView(BaseDomainView): urlname = 'bulk_sms_verification' @method_decorator(require_can_edit_commcare_users) @method_decorator(requires_privilege_with_fallback(privileges.INBOUND_SMS)) def dispatch(self, *args, **kwargs): return super(BulkSMSVerificationView, self).dispatch(*args, **kwargs) def initiate_verification(self, request, group): counts = { 'users': 0, 'phone_numbers': 0, 'phone_numbers_in_use': 0, 'phone_numbers_already_verified': 0, 'phone_numbers_pending_verification': 0, 'workflows_started': 0, } users_with_error = [] for user in group.get_users(is_active=True, only_commcare=True): counts['users'] += 1 for phone_number in user.phone_numbers: counts['phone_numbers'] += 1 try: result = initiate_sms_verification_workflow( user, phone_number) except Exception: result = None if result is None: users_with_error.append(user.raw_username) elif result == VERIFICATION__ALREADY_IN_USE: counts['phone_numbers_in_use'] += 1 elif result == VERIFICATION__ALREADY_VERIFIED: counts['phone_numbers_already_verified'] += 1 elif result == VERIFICATION__RESENT_PENDING: counts['phone_numbers_pending_verification'] += 1 elif result == VERIFICATION__WORKFLOW_STARTED: counts['workflows_started'] += 1 success_msg = _( '%(users)s user(s) and %(phone_numbers)s phone number(s) processed. ' '%(phone_numbers_already_verified)s already verified, ' '%(phone_numbers_in_use)s already in use by other contact(s), ' '%(phone_numbers_pending_verification)s already pending verification, ' 'and %(workflows_started)s verification workflow(s) started.' ) % counts messages.success(request, success_msg) if users_with_error: error_msg = _( 'Error processing the following user(s): %(users)s. Please try ' 'again and if the problem persists, report an issue.') % { 'users': ', '.join(set(users_with_error)) } messages.error(request, error_msg) def get(self, request, *args, **kwargs): raise Http404() def post(self, request, domain, group_id, *args, **kwargs): group = get_group_or_404(domain, group_id) self.initiate_verification(request, group) return HttpResponseRedirect( reverse(EditGroupMembersView.urlname, args=[domain, group_id]))
class CreateCommCareUserModal(JsonRequestResponseMixin, DomainViewMixin, View): template_name = "users/new_mobile_worker_modal.html" urlname = 'new_mobile_worker_modal' @method_decorator(require_can_edit_commcare_users) def dispatch(self, request, *args, **kwargs): if not can_add_extra_mobile_workers(request): raise PermissionDenied() return super(CreateCommCareUserModal, self).dispatch(request, *args, **kwargs) def render_form(self, status): return self.render_json_response({ "status": status, "form_html": render_to_string(self.template_name, { 'form': self.new_commcare_user_form, 'data_fields_form': self.custom_data.form, }, request=self.request) }) def get(self, request, *args, **kwargs): return self.render_form("success") @property @memoized def custom_data(self): return CustomDataEditor( field_view=UserFieldsView, domain=self.domain, post_dict=self.request.POST if self.request.method == "POST" else None, ) @property @memoized def new_commcare_user_form(self): if self.request.method == "POST": data = self.request.POST.dict() form = CommCareAccountForm(data, domain=self.domain) else: form = CommCareAccountForm(domain=self.domain) return form @method_decorator(requires_privilege_with_fallback(privileges.OUTBOUND_SMS)) def post(self, request, *args, **kwargs): if self.new_commcare_user_form.is_valid() and self.custom_data.is_valid(): username = self.new_commcare_user_form.cleaned_data['username'] password = self.new_commcare_user_form.cleaned_data['password'] phone_number = self.new_commcare_user_form.cleaned_data['phone_number'] user = CommCareUser.create( self.domain, username, password, phone_number=phone_number, device_id="Generated from HQ", user_data=self.custom_data.get_data_to_save(), ) if 'location_id' in request.GET: try: loc = SQLLocation.objects.get(domain=self.domain, location_id=request.GET['location_id']) except SQLLocation.DoesNotExist: raise Http404() user.set_location(loc) if phone_number: initiate_sms_verification_workflow(user, phone_number) user_json = {'user_id': user._id, 'text': user.username_in_report} return self.render_json_response({"status": "success", "user": user_json}) return self.render_form("failure")
class BroadcastListView(BaseMessagingSectionView): template_name = 'scheduling/broadcasts_list.html' urlname = 'new_list_broadcasts' page_title = ugettext_lazy('Broadcasts') LIST_SCHEDULED = 'list_scheduled' LIST_IMMEDIATE = 'list_immediate' ACTION_ACTIVATE_SCHEDULED_BROADCAST = 'activate_scheduled_broadcast' ACTION_DEACTIVATE_SCHEDULED_BROADCAST = 'deactivate_scheduled_broadcast' ACTION_DELETE_SCHEDULED_BROADCAST = 'delete_scheduled_broadcast' @method_decorator( requires_privilege_with_fallback(privileges.REMINDERS_FRAMEWORK)) @use_datatables def dispatch(self, *args, **kwargs): return super(BroadcastListView, self).dispatch(*args, **kwargs) @cached_property def project_timezone(self): return get_timezone_for_user(None, self.domain) def _format_time(self, time): if not time: return '' user_time = ServerTime(time).user_time(self.project_timezone) return user_time.ui_string(SERVER_DATETIME_FORMAT) def get_scheduled_ajax_response(self): query = (ScheduledBroadcast.objects.filter(domain=self.domain, deleted=False).order_by( '-last_sent_timestamp', 'id')) total_records = query.count() query = query.select_related('schedule') limit = int(self.request.GET.get('limit', 10)) page = int(self.request.GET.get('page', 1)) skip = (page - 1) * limit broadcasts = [ self._fmt_scheduled_broadcast(broadcast) for broadcast in query[skip:skip + limit] ] return JsonResponse({ 'broadcasts': broadcasts, 'total': total_records, }) def _fmt_scheduled_broadcast(self, broadcast): return { 'name': broadcast.name, 'last_sent': self._format_time(broadcast.last_sent_timestamp), 'active': broadcast.schedule.active, 'editable': self.can_use_inbound_sms or not broadcast.schedule.memoized_uses_sms_survey, 'id': broadcast.id, 'deleted': broadcast.deleted, } def get_immediate_ajax_response(self): query = (ImmediateBroadcast.objects.filter(domain=self.domain, deleted=False).order_by( '-last_sent_timestamp', 'id')) total_records = query.count() limit = int(self.request.GET.get('limit', 10)) page = int(self.request.GET.get('page', 1)) skip = (page - 1) * limit broadcasts = [{ 'name': broadcast.name, 'last_sent': self._format_time(broadcast.last_sent_timestamp), 'id': broadcast.id, } for broadcast in query[skip:skip + limit]] return JsonResponse({ 'broadcasts': broadcasts, 'total': total_records, }) def get_scheduled_broadcast(self, broadcast_id): try: return ScheduledBroadcast.objects.get(domain=self.domain, pk=broadcast_id, deleted=False) except ScheduledBroadcast.DoesNotExist: raise Http404() def get_scheduled_broadcast_activate_ajax_response(self, active_flag, broadcast_id): broadcast = self.get_scheduled_broadcast(broadcast_id) if not self.can_use_inbound_sms and broadcast.schedule.memoized_uses_sms_survey: return HttpResponseBadRequest( "Cannot create or edit survey reminders because subscription " "does not have access to inbound SMS") TimedSchedule.objects.filter(schedule_id=broadcast.schedule_id).update( active=active_flag) refresh_timed_schedule_instances.delay(broadcast.schedule_id, broadcast.recipients, start_date=broadcast.start_date) return JsonResponse({ 'success': True, 'broadcast': self._fmt_scheduled_broadcast(broadcast), }) def get_scheduled_broadcast_delete_ajax_response(self, broadcast_id): broadcast = self.get_scheduled_broadcast(broadcast_id) broadcast.soft_delete() return JsonResponse({ 'success': True, 'broadcast': self._fmt_scheduled_broadcast(broadcast), }) def get(self, request, *args, **kwargs): action = request.GET.get('action') if action == self.LIST_SCHEDULED: return self.get_scheduled_ajax_response() elif action == self.LIST_IMMEDIATE: return self.get_immediate_ajax_response() return super(BroadcastListView, self).get(*args, **kwargs) def post(self, request, *args, **kwargs): action = request.POST.get('action') broadcast_id = request.POST.get('broadcast_id') with get_broadcast_edit_critical_section( EditScheduleView.SCHEDULED_BROADCAST, broadcast_id): if action == self.ACTION_ACTIVATE_SCHEDULED_BROADCAST: return self.get_scheduled_broadcast_activate_ajax_response( True, broadcast_id) elif action == self.ACTION_DEACTIVATE_SCHEDULED_BROADCAST: return self.get_scheduled_broadcast_activate_ajax_response( False, broadcast_id) elif action == self.ACTION_DELETE_SCHEDULED_BROADCAST: return self.get_scheduled_broadcast_delete_ajax_response( broadcast_id) else: return HttpResponseBadRequest()
class CreateScheduleView(BaseMessagingSectionView, AsyncHandlerMixin): urlname = 'create_schedule' page_title = ugettext_lazy('Schedule a Message') template_name = 'scheduling/create_schedule.html' async_handlers = [MessagingRecipientHandler] read_only_mode = False @method_decorator(_requires_new_reminder_framework()) @method_decorator(requires_privilege_with_fallback(privileges.OUTBOUND_SMS) ) @method_decorator(require_permission(Permissions.edit_data)) @use_jquery_ui @use_timepicker @use_select2 def dispatch(self, *args, **kwargs): return super(CreateScheduleView, self).dispatch(*args, **kwargs) @property def parent_pages(self): return [ { 'title': BroadcastListView.page_title, 'url': reverse(BroadcastListView.urlname, args=[self.domain]), }, ] @property def broadcast(self): return None @property def schedule(self): return None @cached_property def schedule_form(self): if self.request.method == 'POST': return BroadcastForm(self.domain, self.schedule, self.broadcast, self.request.POST) return BroadcastForm(self.domain, self.schedule, self.broadcast) @property def page_context(self): return { 'schedule_form': self.schedule_form, 'read_only_mode': self.read_only_mode, } def post(self, request, *args, **kwargs): if self.async_response is not None: return self.async_response if self.schedule_form.is_valid(): broadcast, schedule = self.schedule_form.save_broadcast_and_schedule( ) if isinstance(schedule, AlertSchedule): refresh_alert_schedule_instances.delay(schedule.schedule_id, broadcast.recipients) elif isinstance(schedule, TimedSchedule): refresh_timed_schedule_instances.delay( schedule.schedule_id, broadcast.recipients, start_date=broadcast.start_date) else: raise TypeError("Expected AlertSchedule or TimedSchedule") return HttpResponseRedirect( reverse(BroadcastListView.urlname, args=[self.domain])) return self.get(request, *args, **kwargs)
class CreateConditionalAlertView(BaseMessagingSectionView, AsyncHandlerMixin): urlname = 'create_conditional_alert' page_title = ugettext_lazy('New Conditional Message') template_name = 'scheduling/conditional_alert.html' async_handlers = [MessagingRecipientHandler] @method_decorator(_requires_new_reminder_framework()) @method_decorator(requires_privilege_with_fallback(privileges.OUTBOUND_SMS) ) @method_decorator(require_permission(Permissions.edit_data)) @use_jquery_ui @use_timepicker @use_select2 def dispatch(self, *args, **kwargs): return super(CreateConditionalAlertView, self).dispatch(*args, **kwargs) @property def parent_pages(self): return [ { 'title': ConditionalAlertListView.page_title, 'url': reverse(ConditionalAlertListView.urlname, args=[self.domain]), }, ] @property def page_context(self): return { 'basic_info_form': self.basic_info_form, 'criteria_form': self.criteria_form, 'schedule_form': self.schedule_form, 'read_only_mode': self.read_only_mode, 'criteria_form_active': self.criteria_form.errors or not self.schedule_form.errors, 'schedule_form_active': self.schedule_form.errors and not self.criteria_form.errors, } @cached_property def schedule_form(self): if self.request.method == 'POST': return ConditionalAlertScheduleForm(self.domain, self.schedule, self.rule, self.criteria_form, self.request.POST) return ConditionalAlertScheduleForm(self.domain, self.schedule, self.rule, self.criteria_form) @property def schedule(self): return None @property def rule(self): return None @cached_property def read_only_mode(self): return (not self.is_system_admin and (self.criteria_form.requires_system_admin_to_edit or self.schedule_form.requires_system_admin_to_edit)) @cached_property def is_system_admin(self): return self.request.couch_user.is_superuser @cached_property def basic_info_form(self): if self.request.method == 'POST': return ConditionalAlertForm(self.domain, self.rule, self.request.POST) return ConditionalAlertForm(self.domain, self.rule) @cached_property def criteria_form(self): kwargs = { 'rule': self.rule, 'is_system_admin': self.is_system_admin, } if self.request.method == 'POST': return ConditionalAlertCriteriaForm(self.domain, self.request.POST, **kwargs) return ConditionalAlertCriteriaForm(self.domain, **kwargs) def post(self, request, *args, **kwargs): if self.async_response is not None: return self.async_response basic_info_form_valid = self.basic_info_form.is_valid() criteria_form_valid = self.criteria_form.is_valid() schedule_form_valid = self.schedule_form.is_valid() if self.read_only_mode: # Don't allow making changes to rules that have custom # criteria/actions unless the user has permission to return HttpResponseBadRequest() if basic_info_form_valid and criteria_form_valid and schedule_form_valid: if not self.is_system_admin and ( self.criteria_form.requires_system_admin_to_save or self.schedule_form.requires_system_admin_to_save): # Don't allow adding custom criteria/actions to rules # unless the user has permission to return HttpResponseBadRequest() with transaction.atomic(): if self.rule: rule = self.rule else: rule = AutomaticUpdateRule( domain=self.domain, active=True, migrated=True, workflow=AutomaticUpdateRule.WORKFLOW_SCHEDULING, ) rule.name = self.basic_info_form.cleaned_data['name'] self.criteria_form.save_criteria(rule) self.schedule_form.save_rule_action_and_schedule(rule) initiate_messaging_rule_run(rule.domain, rule.pk) return HttpResponseRedirect( reverse(ConditionalAlertListView.urlname, args=[self.domain])) return self.get(request, *args, **kwargs)
class UploadCommCareUsers(BaseManageCommCareUserView): template_name = 'hqwebapp/bulk_upload.html' urlname = 'upload_commcare_users' page_title = ugettext_noop("Bulk Upload Mobile Workers") @method_decorator(requires_privilege_with_fallback(privileges.BULK_USER_MANAGEMENT)) def dispatch(self, request, *args, **kwargs): return super(UploadCommCareUsers, self).dispatch(request, *args, **kwargs) @property def page_context(self): request_params = self.request.GET if self.request.method == 'GET' else self.request.POST context = { 'bulk_upload': { "help_site": { "address": BULK_MOBILE_HELP_SITE, "name": _("CommCare Help Site"), }, "download_url": reverse( "download_commcare_users", args=(self.domain,)), "adjective": _("mobile worker"), "plural_noun": _("mobile workers"), }, 'show_secret_settings': request_params.get("secret", False), } context.update({ 'bulk_upload_form': get_bulk_upload_form(context), }) return context def post(self, request, *args, **kwargs): """View's dispatch method automatically calls this""" try: self.workbook = get_workbook(request.FILES.get('bulk_upload_file')) except WorkbookJSONError as e: messages.error(request, six.text_type(e)) return self.get(request, *args, **kwargs) try: self.user_specs = self.workbook.get_worksheet(title='users') except WorksheetNotFound: try: self.user_specs = self.workbook.get_worksheet() except WorksheetNotFound: return HttpResponseBadRequest("Workbook has no worksheets") try: self.group_specs = self.workbook.get_worksheet(title='groups') except WorksheetNotFound: self.group_specs = [] try: check_headers(self.user_specs) except UserUploadError as e: messages.error(request, _(six.text_type(e))) return HttpResponseRedirect(reverse(UploadCommCareUsers.urlname, args=[self.domain])) # convert to list here because iterator destroys the row once it has # been read the first time self.user_specs = list(self.user_specs) for user_spec in self.user_specs: try: user_spec['username'] = enforce_string_type(user_spec['username']) except StringTypeRequiredError: messages.error( request, _("Error: Expected username to be a Text type for username {0}") .format(user_spec['username']) ) return HttpResponseRedirect(reverse(UploadCommCareUsers.urlname, args=[self.domain])) try: check_existing_usernames(self.user_specs, self.domain) except UserUploadError as e: messages.error(request, _(six.text_type(e))) return HttpResponseRedirect(reverse(UploadCommCareUsers.urlname, args=[self.domain])) try: check_duplicate_usernames(self.user_specs) except UserUploadError as e: messages.error(request, _(six.text_type(e))) return HttpResponseRedirect(reverse(UploadCommCareUsers.urlname, args=[self.domain])) task_ref = expose_cached_download(payload=None, expiry=1*60*60, file_extension=None) task = bulk_upload_async.delay( self.domain, self.user_specs, list(self.group_specs), ) task_ref.set_task(task) return HttpResponseRedirect( reverse( UserUploadStatusView.urlname, args=[self.domain, task_ref.download_id] ) )
from django.utils.decorators import method_decorator from django_prbac.utils import has_privilege from corehq import privileges from corehq.apps.accounting.decorators import requires_privilege_with_fallback from corehq.apps.reports.dispatcher import ReportDispatcher, datespan_default from corehq.apps.users.decorators import require_permission from corehq.apps.users.models import Permissions from custom.icds_core.view_utils import check_data_interfaces_blocked_for_domain require_can_edit_data = require_permission(Permissions.edit_data) require_form_management_privilege = requires_privilege_with_fallback( privileges.DATA_CLEANUP) class EditDataInterfaceDispatcher(ReportDispatcher): prefix = 'edit_data_interface' map_name = 'EDIT_DATA_INTERFACES' @method_decorator(require_can_edit_data) @method_decorator(check_data_interfaces_blocked_for_domain) @datespan_default def dispatch(self, request, *args, **kwargs): from corehq.apps.case_importer.base import ImportCases from .interfaces import BulkFormManagementInterface if kwargs['report_slug'] == ImportCases.slug: return self.bulk_import_case_dispatch(request, *args, **kwargs) elif (kwargs['report_slug'] == BulkFormManagementInterface.slug
class BaseRepeaterView(BaseAdminProjectSettingsView): page_title = ugettext_lazy("Forward Data") repeater_form_class = GenericRepeaterForm template_name = 'repeaters/add_form_repeater.html' @method_decorator(require_permission(Permissions.edit_motech)) @method_decorator( requires_privilege_with_fallback(privileges.DATA_FORWARDING)) def dispatch(self, request, *args, **kwargs): return super(BaseRepeaterView, self).dispatch(request, *args, **kwargs) @property def page_url(self): return reverse(self.urlname, args=[self.domain, self.repeater_type]) @property def parent_pages(self): return [{ 'title': DomainForwardingOptionsView.page_title, 'url': reverse(DomainForwardingOptionsView.urlname, args=[self.domain]), }] @property def repeater_type(self): return self.kwargs['repeater_type'] @property def page_name(self): return self.repeater_class.friendly_name @property @memoized def repeater_class(self): try: return get_all_repeater_types()[self.repeater_type] except KeyError: raise Http404("No such repeater {}. Valid types: {}".format( self.repeater_type, list(get_all_repeater_types()))) @property def add_repeater_form(self): return None @property def page_context(self): return { 'form': self.add_repeater_form, 'repeater_type': self.repeater_type, } def initialize_repeater(self): raise NotImplementedError def make_repeater(self): repeater = self.initialize_repeater() return self.set_repeater_attr(repeater, self.add_repeater_form.cleaned_data) def set_repeater_attr(self, repeater, cleaned_data): repeater.domain = self.domain repeater.connection_settings_id = int( cleaned_data['connection_settings_id']) repeater.format = cleaned_data['format'] return repeater def post_save(self, request, repeater): pass def post(self, request, *args, **kwargs): if self.add_repeater_form.is_valid(): repeater = self.make_repeater() repeater.save() return self.post_save(request, repeater) return self.get(request, *args, **kwargs)
page_title = gettext_lazy("Create Dashboard Feed") @location_safe class CreateNewDailySavedCaseExport(DailySavedExportMixin, CreateNewCustomCaseExportView): urlname = 'new_case_daily_saved_export' metric_name = 'Daily Saved Case Export' @location_safe class CreateNewDailySavedFormExport(DailySavedExportMixin, CreateNewCustomFormExportView): urlname = 'new_form_faily_saved_export' metric_name = 'Daily Saved Form Export' @method_decorator(requires_privilege_with_fallback(privileges.ODATA_FEED), name='dispatch') class CreateODataCaseFeedView(ODataFeedMixin, CreateNewCustomCaseExportView): urlname = 'new_odata_case_feed' page_title = gettext_lazy("Create OData Case Feed") metric_name = 'PowerBI Case Export' def create_new_export_instance(self, schema, username, export_settings=None): export_instance = super().create_new_export_instance( schema, username, export_settings=export_settings ) clean_odata_columns(export_instance) return export_instance
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, *args, **kwargs): return super(BroadcastListView, self).dispatch(*args, **kwargs) @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: return super(BroadcastListView, self).get(*args, **kwargs) def post(self, *args, **kwargs): action = self.request.POST.get('action') if action == self.DELETE_BROADCAST: return self.delete_broadcast(self.request.POST.get('broadcast_id', None)) else: 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 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, *args, **kwargs): return super(BaseMessagingSectionView, self).dispatch(*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), } @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 post(self, *args, **kwargs): action = self.request.POST.get('action') if action in [ACTION_ACTIVATE, ACTION_DEACTIVATE, ACTION_DELETE]: return HttpResponse(json.dumps(self.get_action_response(action))) return HttpResponse(status=400)
from corehq.apps.users.decorators import require_permission from corehq.apps.users.models import CommCareUser, Permissions from corehq.apps.users.util import normalize_username from couchexport.export import UnsupportedExportFormat, export_raw from couchexport.models import Format from couchexport.shortcuts import export_response from dimagi.utils.couch.bulk import CouchTransaction from dimagi.utils.excel import WorkbookJSONReader, WorksheetNotFound from dimagi.utils.logging import notify_exception from dimagi.utils.web import json_response from dimagi.utils.decorators.view import get_file require_can_edit_fixtures = lambda *args, **kwargs: ( require_permission(Permissions.edit_data)( requires_privilege_with_fallback(privileges.LOOKUP_TABLES)(*args, **kwargs) ) ) def strip_json(obj, disallow_basic=None, disallow=None): disallow = disallow or [] if disallow_basic is None: disallow_basic = ['_rev', 'domain', 'doc_type'] disallow += disallow_basic ret = {} try: obj = obj.to_json() except Exception: pass for key in obj:
class KeywordsListView(BaseMessagingSectionView, CRUDPaginatedViewMixin): template_name = 'reminders/keyword_list.html' urlname = 'keyword_list' page_title = ugettext_noop("Keywords") limit_text = ugettext_noop("keywords per page") empty_notification = ugettext_noop("You have no keywords. Please add one!") loading_message = ugettext_noop("Loading keywords...") @use_multiselect @method_decorator(requires_privilege_with_fallback(privileges.INBOUND_SMS)) def dispatch(self, *args, **kwargs): return super(KeywordsListView, self).dispatch(*args, **kwargs) @property def page_url(self): return reverse(self.urlname, args=[self.domain]) @property @memoized def total(self): return Keyword.get_by_domain(self.domain).count() @property def column_names(self): return [ _("Keyword"), _("Description"), _("Action"), ] @property def page_context(self): context = self.pagination_context context['linked_domains'] = [ domain_link.linked_domain for domain_link in get_linked_domains(self.domain) ] context['linkable_keywords'] = self._linkable_keywords() return context def _linkable_keywords(self): LinkableKeyword = namedtuple('LinkableKeyword', 'keyword can_be_linked') linkable_keywords = [] for keyword in self._all_keywords(): sends_to_usergroup = keyword.keywordaction_set.filter( recipient=KeywordAction.RECIPIENT_USER_GROUP).count() if sends_to_usergroup: linkable_keywords.append(LinkableKeyword(keyword, False)) else: linkable_keywords.append(LinkableKeyword(keyword, True)) return linkable_keywords @memoized def _all_keywords(self): return Keyword.get_by_domain(self.domain) @property def paginated_list(self): for keyword in self._all_keywords()[self.skip:self.skip + self.limit]: yield { 'itemData': self._fmt_keyword_data(keyword), 'template': 'keyword-row-template', } def _fmt_keyword_data(self, keyword): return { 'id': keyword.couch_id, 'keyword': keyword.keyword, 'description': keyword.description, 'editUrl': reverse(EditStructuredKeywordView.urlname, args=[self.domain, keyword.couch_id]) if keyword.is_structured_sms() else reverse( EditNormalKeywordView.urlname, args=[self.domain, keyword.couch_id]), 'deleteModalId': 'delete-%s' % keyword.couch_id, } def _fmt_deleted_keyword_data(self, keyword): return { 'keyword': keyword.keyword, 'description': keyword.description, } def get_deleted_item_data(self, item_id): try: k = Keyword.objects.get(couch_id=item_id) except Keyword.DoesNotExist: raise Http404() if k.domain != self.domain: raise Http404() k.delete() return { 'itemData': self._fmt_deleted_keyword_data(k), 'template': 'keyword-deleted-template', } def post(self, *args, **kwargs): return self.paginate_crud_response
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): 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)
from corehq.const import SERVER_DATETIME_FORMAT from corehq.util.timezones.conversions import ServerTime from corehq.util.timezones.utils import get_timezone_for_user from custom.ewsghana.forms import EWSBroadcastForm from dimagi.utils.couch.cache.cache_core import get_redis_client from memoized import memoized from dimagi.utils.logging import notify_exception ACTION_ACTIVATE = 'activate' ACTION_DEACTIVATE = 'deactivate' ACTION_DELETE = 'delete' survey_reminders_permission = lambda *args, **kwargs: ( require_permission(Permissions.edit_data) (requires_privilege_with_fallback(privileges.INBOUND_SMS) (*args, **kwargs))) def add_migration_in_progress_message(request): messages.warning( request, _("Maintenance is underway to upgrade your experience with " "messaging reminders. As a result, pages which create or " "edit reminders are currently unavailable. Please check " "back soon.")) def get_project_time_info(domain): timezone = get_timezone_for_user(None, domain) now = pytz.utc.localize(datetime.utcnow()) timezone_now = now.astimezone(timezone)
class ConditionalAlertListView(BaseMessagingSectionView, DataTablesAJAXPaginationMixin): template_name = 'scheduling/conditional_alert_list.html' urlname = 'conditional_alert_list' page_title = ugettext_lazy('Schedule a Conditional Message') LIST_CONDITIONAL_ALERTS = 'list_conditional_alerts' ACTION_ACTIVATE = 'activate' ACTION_DEACTIVATE = 'deactivate' ACTION_DELETE = 'delete' @method_decorator(_requires_new_reminder_framework()) @method_decorator(requires_privilege_with_fallback(privileges.OUTBOUND_SMS) ) @method_decorator(require_permission(Permissions.edit_data)) @use_datatables def dispatch(self, *args, **kwargs): return super(ConditionalAlertListView, self).dispatch(*args, **kwargs) def get_conditional_alerts_queryset(self): return (AutomaticUpdateRule.objects.filter( domain=self.domain, workflow=AutomaticUpdateRule.WORKFLOW_SCHEDULING, deleted=False).order_by('case_type', 'name', 'id')) def get_conditional_alerts_ajax_response(self): query = self.get_conditional_alerts_queryset() total_records = query.count() rules = query[self.display_start:self.display_start + self.display_length] data = [] for rule in rules: data.append([ '< delete placeholder >', rule.name, rule.case_type, rule.get_messaging_rule_schedule().active, '< action placeholder >', rule.locked_for_editing, MessagingRuleProgressHelper(rule.pk).get_progress_pct(), rule.pk, ]) return self.datatables_ajax_response(data, total_records) def get(self, request, *args, **kwargs): action = request.GET.get('action') if action == self.LIST_CONDITIONAL_ALERTS: return self.get_conditional_alerts_ajax_response() return super(ConditionalAlertListView, self).get(*args, **kwargs) def get_rule(self, rule_id): try: return AutomaticUpdateRule.objects.get( domain=self.domain, pk=rule_id, deleted=False, workflow=AutomaticUpdateRule.WORKFLOW_SCHEDULING) except AutomaticUpdateRule.DoesNotExist: raise Http404() def get_activate_ajax_response(self, active_flag, rule): """ When we deactivate a conditional alert from the UI, we are only deactivating the schedule that sends the content. The rule itself stays active. This is because we want to be keeping all the schedule instances up to date (though inactive), so that if the schedule is reactivated, we don't send a large quantity of stale messages. """ with transaction.atomic(): schedule = rule.get_messaging_rule_schedule() schedule.active = active_flag schedule.save() initiate_messaging_rule_run(self.domain, rule.pk) return HttpResponse() def get_delete_ajax_response(self, rule): rule.soft_delete() return HttpResponse() def post(self, request, *args, **kwargs): action = request.POST.get('action') rule_id = request.POST.get('rule_id') with get_conditional_alert_edit_critical_section(rule_id): rule = self.get_rule(rule_id) if rule.locked_for_editing: return HttpResponseBadRequest() if action == self.ACTION_ACTIVATE: return self.get_activate_ajax_response(True, rule) elif action == self.ACTION_DEACTIVATE: return self.get_activate_ajax_response(False, rule) elif action == self.ACTION_DELETE: return self.get_delete_ajax_response(rule) else: return HttpResponseBadRequest()
form_query_string_pending = _change_record_state( self.request.GET, 'PENDING').urlencode() context.update( total=total, total_pending=total_pending, total_cancelled=total_cancelled, form_query_string=form_query_string, form_query_string_pending=form_query_string_pending, form_query_string_cancelled=form_query_string_cancelled, ) return context @method_decorator(domain_admin_required, name='dispatch') @method_decorator(requires_privilege_with_fallback(privileges.DATA_FORWARDING), name='dispatch') class RepeatRecordView(View): urlname = 'repeat_record' http_method_names = ['get', 'post'] @staticmethod def get_record_or_404(domain, record_id): try: record = RepeatRecord.get(record_id) except ResourceNotFound: raise Http404() if record.domain != domain: raise Http404()
class BroadcastListView(BaseMessagingSectionView, DataTablesAJAXPaginationMixin): template_name = 'scheduling/broadcasts_list.html' urlname = 'new_list_broadcasts' page_title = ugettext_lazy('Schedule a Message') LIST_SCHEDULED = 'list_scheduled' LIST_IMMEDIATE = 'list_immediate' ACTION_ACTIVATE_SCHEDULED_BROADCAST = 'activate_scheduled_broadcast' ACTION_DEACTIVATE_SCHEDULED_BROADCAST = 'deactivate_scheduled_broadcast' ACTION_DELETE_SCHEDULED_BROADCAST = 'delete_scheduled_broadcast' @method_decorator(_requires_new_reminder_framework()) @method_decorator(requires_privilege_with_fallback(privileges.OUTBOUND_SMS) ) @method_decorator(require_permission(Permissions.edit_data)) @use_datatables def dispatch(self, *args, **kwargs): return super(BroadcastListView, self).dispatch(*args, **kwargs) @cached_property def project_timezone(self): return get_timezone_for_user(None, self.domain) def _format_time(self, time): if not time: return '' user_time = ServerTime(time).user_time(self.project_timezone) return user_time.ui_string(SERVER_DATETIME_FORMAT) def get_scheduled_ajax_response(self): query = (ScheduledBroadcast.objects.filter(domain=self.domain, deleted=False).order_by( '-last_sent_timestamp', 'id')) total_records = query.count() query = query.select_related('schedule') broadcasts = query[self.display_start:self.display_start + self.display_length] data = [] for broadcast in broadcasts: data.append([ '< delete placeholder >', broadcast.name, self._format_time(broadcast.last_sent_timestamp), broadcast.schedule.active, '< action placeholder >', broadcast.id, ]) return self.datatables_ajax_response(data, total_records) def get_immediate_ajax_response(self): query = (ImmediateBroadcast.objects.filter(domain=self.domain, deleted=False).order_by( '-last_sent_timestamp', 'id')) total_records = query.count() broadcasts = query[self.display_start:self.display_start + self.display_length] data = [] for broadcast in broadcasts: data.append([ broadcast.name, self._format_time(broadcast.last_sent_timestamp), broadcast.id, ]) return self.datatables_ajax_response(data, total_records) def get_scheduled_broadcast(self, broadcast_id): try: return ScheduledBroadcast.objects.get(domain=self.domain, pk=broadcast_id, deleted=False) except ScheduledBroadcast.DoesNotExist: raise Http404() def get_scheduled_broadcast_activate_ajax_response(self, active_flag, broadcast_id): broadcast = self.get_scheduled_broadcast(broadcast_id) TimedSchedule.objects.filter(schedule_id=broadcast.schedule_id).update( active=active_flag) refresh_timed_schedule_instances.delay(broadcast.schedule_id, broadcast.recipients, start_date=broadcast.start_date) return HttpResponse() def get_scheduled_broadcast_delete_ajax_response(self, broadcast_id): broadcast = self.get_scheduled_broadcast(broadcast_id) broadcast.soft_delete() return HttpResponse() def get(self, request, *args, **kwargs): action = request.GET.get('action') if action == self.LIST_SCHEDULED: return self.get_scheduled_ajax_response() elif action == self.LIST_IMMEDIATE: return self.get_immediate_ajax_response() return super(BroadcastListView, self).get(*args, **kwargs) def post(self, request, *args, **kwargs): action = request.POST.get('action') broadcast_id = request.POST.get('broadcast_id') with get_broadcast_edit_critical_section( EditScheduleView.SCHEDULED_BROADCAST, broadcast_id): if action == self.ACTION_ACTIVATE_SCHEDULED_BROADCAST: return self.get_scheduled_broadcast_activate_ajax_response( True, broadcast_id) elif action == self.ACTION_DEACTIVATE_SCHEDULED_BROADCAST: return self.get_scheduled_broadcast_activate_ajax_response( False, broadcast_id) elif action == self.ACTION_DELETE_SCHEDULED_BROADCAST: return self.get_scheduled_broadcast_delete_ajax_response( broadcast_id) else: return HttpResponseBadRequest()
from corehq import privileges from corehq.apps.accounting.decorators import requires_privilege_with_fallback from corehq.apps.reports.dispatcher import ProjectReportDispatcher from django.utils.decorators import method_decorator from corehq.apps.users.decorators import require_permission from corehq.apps.users.models import Permissions require_can_edit_fixtures = lambda *args, **kwargs: ( require_permission(Permissions.edit_data) (requires_privilege_with_fallback(privileges.LOOKUP_TABLES) (*args, **kwargs))) class FixtureInterfaceDispatcher(ProjectReportDispatcher): prefix = 'fixture_interface' map_name = 'FIXTURE_INTERFACES' @method_decorator(require_can_edit_fixtures) def dispatch(self, request, *args, **kwargs): return super(FixtureInterfaceDispatcher, self).dispatch(request, *args, **kwargs)
class UploadCommCareUsers(BaseManageCommCareUserView): template_name = 'users/upload_commcare_users.html' urlname = 'upload_commcare_users' page_title = ugettext_noop("Bulk Upload Mobile Workers") @method_decorator(requires_privilege_with_fallback(privileges.BULK_USER_MANAGEMENT)) def dispatch(self, request, *args, **kwargs): return super(UploadCommCareUsers, self).dispatch(request, *args, **kwargs) @property def page_context(self): request_params = self.request.GET if self.request.method == 'GET' else self.request.POST context = { 'bulk_upload': { "help_site": { "address": BULK_MOBILE_HELP_SITE, "name": _("CommCare Help Site"), }, "download_url": reverse( "download_commcare_users", args=(self.domain,)), "adjective": _("mobile worker"), "plural_noun": _("mobile workers"), }, 'show_secret_settings': request_params.get("secret", False), } context.update({ 'bulk_upload_form': get_bulk_upload_form(context), }) return context def post(self, request, *args, **kwargs): """View's dispatch method automatically calls this""" upload = request.FILES.get('bulk_upload_file') try: self.workbook = WorkbookJSONReader(upload) except InvalidExcelFileException: try: csv.DictReader(io.StringIO(upload.read().decode('ascii'), newline=None)) return HttpResponseBadRequest( "CommCare HQ no longer supports CSV upload. " "Please convert to Excel 2007 or higher (.xlsx) " "and try again." ) except UnicodeDecodeError: return HttpResponseBadRequest("Unrecognized format") except JSONReaderError as e: messages.error(request, 'Your upload was unsuccessful. %s' % e.message) return self.get(request, *args, **kwargs) except HeaderValueError as e: return HttpResponseBadRequest("Upload encountered a data type error: %s" % e.message) try: self.user_specs = self.workbook.get_worksheet(title='users') except WorksheetNotFound: try: self.user_specs = self.workbook.get_worksheet() except WorksheetNotFound: return HttpResponseBadRequest("Workbook has no worksheets") try: self.group_specs = self.workbook.get_worksheet(title='groups') except WorksheetNotFound: self.group_specs = [] try: check_headers(self.user_specs) except UserUploadError as e: messages.error(request, _(e.message)) return HttpResponseRedirect(reverse(UploadCommCareUsers.urlname, args=[self.domain])) # convert to list here because iterator destroys the row once it has # been read the first time self.user_specs = list(self.user_specs) for user_spec in self.user_specs: try: user_spec['username'] = enforce_string_type(user_spec['username']) except StringTypeRequiredError: messages.error( request, _("Error: Expected username to be a Text type for username {0}") .format(user_spec['username']) ) return HttpResponseRedirect(reverse(UploadCommCareUsers.urlname, args=[self.domain])) try: check_existing_usernames(self.user_specs, self.domain) except UserUploadError as e: messages.error(request, _(e.message)) return HttpResponseRedirect(reverse(UploadCommCareUsers.urlname, args=[self.domain])) try: check_duplicate_usernames(self.user_specs) except UserUploadError as e: messages.error(request, _(e.message)) return HttpResponseRedirect(reverse(UploadCommCareUsers.urlname, args=[self.domain])) task_ref = expose_cached_download(payload=None, expiry=1*60*60, file_extension=None) task = bulk_upload_async.delay( self.domain, self.user_specs, list(self.group_specs), ) task_ref.set_task(task) return HttpResponseRedirect( reverse( UserUploadStatusView.urlname, args=[self.domain, task_ref.download_id] ) )
page_title = ugettext_lazy("Create Dashboard Feed") @location_safe class CreateNewDailySavedCaseExport(DailySavedExportMixin, CreateNewCustomCaseExportView): urlname = 'new_case_daily_saved_export' @location_safe class CreateNewDailySavedFormExport(DailySavedExportMixin, CreateNewCustomFormExportView): urlname = 'new_form_faily_saved_export' @method_decorator(requires_privilege_with_fallback(privileges.ODATA_FEED), name='dispatch') class CreateODataCaseFeedView(ODataFeedMixin, CreateNewCustomCaseExportView): urlname = 'new_odata_case_feed' page_title = ugettext_lazy("Create OData Case Feed") def create_new_export_instance(self, schema): export_instance = super(CreateODataCaseFeedView, self).create_new_export_instance(schema) clean_odata_columns(export_instance) return export_instance @method_decorator(requires_privilege_with_fallback(privileges.ODATA_FEED), name='dispatch') class CreateODataFormFeedView(ODataFeedMixin, CreateNewCustomFormExportView):
class CreateScheduleView(BaseMessagingSectionView, AsyncHandlerMixin): urlname = 'create_schedule' page_title = ugettext_lazy('New Broadcast') template_name = 'scheduling/create_schedule.html' async_handlers = [MessagingRecipientHandler] read_only_mode = False @method_decorator( requires_privilege_with_fallback(privileges.REMINDERS_FRAMEWORK)) @use_jquery_ui @use_timepicker def dispatch(self, *args, **kwargs): return super(CreateScheduleView, self).dispatch(*args, **kwargs) @property def parent_pages(self): return [ { 'title': BroadcastListView.page_title, 'url': reverse(BroadcastListView.urlname, args=[self.domain]), }, ] @property def broadcast(self): return None @property def schedule(self): return None @cached_property def schedule_form(self): args = [ self.domain, self.schedule, self.can_use_inbound_sms, self.broadcast ] if self.request.method == 'POST': args.append(self.request.POST) return BroadcastForm(*args, is_system_admin=self.is_system_admin) @property def page_context(self): context = super().page_context context.update({ 'schedule_form': self.schedule_form, 'read_only_mode': self.read_only_mode, }) return context def post(self, request, *args, **kwargs): if self.async_response is not None: return self.async_response if self.schedule_form.is_valid(): if not self.can_use_inbound_sms and self.schedule_form.uses_sms_survey: return HttpResponseBadRequest( "Cannot create or edit survey reminders because subscription " "does not have access to inbound SMS") broadcast, schedule = self.schedule_form.save_broadcast_and_schedule( ) if isinstance(schedule, AlertSchedule): refresh_alert_schedule_instances.delay(schedule.schedule_id, broadcast.recipients) elif isinstance(schedule, TimedSchedule): refresh_timed_schedule_instances.delay( schedule.schedule_id, broadcast.recipients, start_date=broadcast.start_date) else: raise TypeError("Expected AlertSchedule or TimedSchedule") return HttpResponseRedirect( reverse(BroadcastListView.urlname, args=[self.domain])) return self.get(request, *args, **kwargs)
class ArchiveFormView(DataInterfaceSection): template_name = 'data_interfaces/interfaces/import_forms.html' urlname = 'archive_forms' page_title = ugettext_noop("Bulk Archive Forms") ONE_MB = 1000000 MAX_SIZE = 3 * ONE_MB @method_decorator( requires_privilege_with_fallback(privileges.BULK_CASE_MANAGEMENT)) def dispatch(self, request, *args, **kwargs): if not toggles.BULK_ARCHIVE_FORMS.enabled(request.user.username): raise Http404() return super(ArchiveFormView, self).dispatch(request, *args, **kwargs) @property def page_url(self): return reverse(self.urlname, args=[self.domain]) @property def page_context(self): context = {} context.update({ 'bulk_upload': { "download_url": static('data_interfaces/xlsx/forms_bulk_example.xlsx'), "adjective": _("example"), "verb": _("archive"), "plural_noun": _("forms"), }, }) context.update({ 'bulk_upload_form': get_bulk_upload_form(context), }) return context @property @memoized def uploaded_file(self): try: bulk_file = self.request.FILES['bulk_upload_file'] if bulk_file.size > self.MAX_SIZE: raise BulkUploadCasesException( _(u"File size too large. " "Please upload file less than" " {size} Megabytes").format(size=self.MAX_SIZE / self.ONE_MB)) except KeyError: raise BulkUploadCasesException(_("No files uploaded")) try: return WorkbookJSONReader(bulk_file) except InvalidExcelFileException: try: csv.DictReader( io.StringIO(bulk_file.read().decode('utf-8'), newline=None)) raise BulkUploadCasesException( _("CommCare HQ does not support that file type." "Please convert to Excel 2007 or higher (.xlsx) " "and try again.")) except UnicodeDecodeError: raise BulkUploadCasesException(_("Unrecognized format")) except JSONReaderError as e: raise BulkUploadCasesException( _('Your upload was unsuccessful. %s') % e.message) def process(self): try: bulk_archive_forms.delay(self.domain, self.request.couch_user, list(self.uploaded_file.get_worksheet())) messages.success( self.request, _("We received your file and are processing it. " "You will receive an email when it has finished.")) except BulkUploadCasesException as e: messages.error(self.request, e.message) return None def post(self, request, *args, **kwargs): self.process() return HttpResponseRedirect(self.page_url)
class AutomaticUpdateRuleListView(DataInterfaceSection, CRUDPaginatedViewMixin): template_name = 'data_interfaces/list_automatic_update_rules.html' urlname = 'automatic_update_rule_list' page_title = ugettext_lazy("Automatically Close Cases") limit_text = ugettext_lazy("rules per page") empty_notification = ugettext_lazy("You have no case rules.") loading_message = ugettext_lazy("Loading rules...") deleted_items_header = ugettext_lazy("Deleted Rules") ACTION_ACTIVATE = 'activate' ACTION_DEACTIVATE = 'deactivate' @method_decorator(requires_privilege_with_fallback(privileges.DATA_CLEANUP)) def dispatch(self, *args, **kwargs): return super(AutomaticUpdateRuleListView, self).dispatch(*args, **kwargs) @property def parameters(self): return self.request.POST if self.request.method == 'POST' else self.request.GET @property def allowed_actions(self): actions = super(AutomaticUpdateRuleListView, self).allowed_actions actions.append(self.ACTION_ACTIVATE) actions.append(self.ACTION_DEACTIVATE) return actions @property def page_context(self): context = self.pagination_context context['help_site_url'] = 'https://confluence.dimagi.com/display/commcarepublic/Automatically+Close+Cases' return context @property def total(self): return self._rules().count() @property def column_names(self): return [ _("Name"), _("Case Type"), _("Status"), _("Last Run"), _("Action"), ] @property @memoized def project_timezone(self): return get_timezone_for_user(None, self.domain) def _format_rule(self, rule): return { 'id': rule.pk, 'name': rule.name, 'case_type': rule.case_type, 'active': rule.active, 'last_run': (ServerTime(rule.last_run) .user_time(self.project_timezone) .done() .strftime(SERVER_DATETIME_FORMAT)) if rule.last_run else '-', 'edit_url': reverse(EditCaseRuleView.urlname, args=[self.domain, rule.pk]), 'action_error': "", # must be provided because knockout template looks for it } @property def paginated_list(self): for rule in self._rules()[self.skip:self.skip + self.limit]: yield { 'itemData': self._format_rule(rule), 'template': 'base-rule-template', } @memoized def _rules(self): return AutomaticUpdateRule.by_domain( self.domain, AutomaticUpdateRule.WORKFLOW_CASE_UPDATE, active_only=False, ).order_by('name', 'id') def post(self, *args, **kwargs): return self.paginate_crud_response def _get_rule(self, rule_id): if rule_id is None: return None, _("Please provide an id.") try: rule = AutomaticUpdateRule.objects.get(pk=rule_id, workflow=AutomaticUpdateRule.WORKFLOW_CASE_UPDATE) except AutomaticUpdateRule.DoesNotExist: return None, _("Rule not found.") if rule.domain != self.domain: return None, _("Rule not found.") return rule, None def get_deleted_item_data(self, rule_id): (rule, error) = self._get_rule(rule_id) if rule is None: return {'success': False, 'error': error} rule.soft_delete() return { 'itemData': { 'name': rule.name, }, 'template': 'rule-deleted-template', } def update_rule(self): (rule, error) = self._get_rule(self.parameters.get('id')) if rule is None: return {'success': False, 'error': error} if self.action == self.ACTION_ACTIVATE: rule.activate() elif self.action == self.ACTION_DEACTIVATE: rule.activate(False) return {'success': True, 'itemData': self._format_rule(rule)} @property def activate_response(self): return self.update_rule() @property def deactivate_response(self): return self.update_rule()
class AutomaticUpdateRuleListView(JSONResponseMixin, DataInterfaceSection): template_name = 'data_interfaces/list_automatic_update_rules.html' urlname = 'automatic_update_rule_list' page_title = ugettext_lazy("Automatically Close Cases") ACTION_ACTIVATE = 'activate' ACTION_DEACTIVATE = 'deactivate' ACTION_DELETE = 'delete' @property @memoized def project_timezone(self): return get_timezone_for_user(None, self.domain) @use_angular_js @method_decorator(requires_privilege_with_fallback(privileges.DATA_CLEANUP) ) def dispatch(self, *args, **kwargs): return super(AutomaticUpdateRuleListView, self).dispatch(*args, **kwargs) @property def page_context(self): return { 'pagination_limit_cookie_name': ('hq.pagination.limit' '.automatic_update_rule_list.%s' % self.domain), 'help_site_url': 'https://confluence.dimagi.com/display/commcarepublic/Automatically+Close+Cases', } def _format_rule(self, rule): return { 'id': rule.pk, 'name': rule.name, 'case_type': rule.case_type, 'active': rule.active, 'last_run': (ServerTime(rule.last_run).user_time( self.project_timezone).done().strftime(SERVER_DATETIME_FORMAT)) if rule.last_run else '-', 'edit_url': reverse(EditCaseRuleView.urlname, args=[self.domain, rule.pk]), } @allow_remote_invocation def get_pagination_data(self, in_data): try: limit = int(in_data['limit']) page = int(in_data['page']) except (TypeError, KeyError, ValueError): return { 'success': False, 'error': _("Please provide pagination info."), } start = (page - 1) * limit stop = limit * page rules = AutomaticUpdateRule.by_domain( self.domain, AutomaticUpdateRule.WORKFLOW_CASE_UPDATE, active_only=False, ) rule_page = rules.order_by('name')[start:stop] total = rules.count() return { 'response': { 'itemList': list(map(self._format_rule, rule_page)), 'total': total, 'page': page, }, 'success': True, } @allow_remote_invocation def update_rule(self, in_data): try: rule_id = in_data['id'] except KeyError: return { 'error': _("Please provide an id."), } try: action = in_data['update_action'] except KeyError: return { 'error': _("Please provide an update_action."), } if action not in ( self.ACTION_ACTIVATE, self.ACTION_DEACTIVATE, self.ACTION_DELETE, ): return { 'error': _("Unrecognized update_action."), } try: rule = AutomaticUpdateRule.objects.get( pk=rule_id, workflow=AutomaticUpdateRule.WORKFLOW_CASE_UPDATE) except AutomaticUpdateRule.DoesNotExist: return { 'error': _("Rule not found."), } if rule.domain != self.domain: return { 'error': _("Rule not found."), } if action == self.ACTION_ACTIVATE: rule.activate() elif action == self.ACTION_DEACTIVATE: rule.activate(False) elif action == self.ACTION_DELETE: rule.soft_delete() return { 'success': True, }
class KeywordsListView(BaseMessagingSectionView, CRUDPaginatedViewMixin): template_name = 'reminders/keyword_list.html' urlname = 'keyword_list' page_title = ugettext_noop("Keywords") limit_text = ugettext_noop("keywords per page") empty_notification = ugettext_noop("You have no keywords. Please add one!") loading_message = ugettext_noop("Loading keywords...") @method_decorator(requires_privilege_with_fallback(privileges.INBOUND_SMS)) def dispatch(self, *args, **kwargs): return super(KeywordsListView, self).dispatch(*args, **kwargs) @property def page_url(self): return reverse(self.urlname, args=[self.domain]) @property def parameters(self): return self.request.POST if self.request.method == 'POST' else self.request.GET @property @memoized def total(self): return Keyword.get_by_domain(self.domain).count() @property def column_names(self): return [ _("Keyword"), _("Description"), _("Action"), ] @property def page_context(self): return self.pagination_context @property def paginated_list(self): for keyword in Keyword.get_by_domain( self.domain, limit=self.limit, skip=self.skip, ): yield { 'itemData': self._fmt_keyword_data(keyword), 'template': 'keyword-row-template', } def _fmt_keyword_data(self, keyword): return { 'id': keyword.couch_id, 'keyword': keyword.keyword, 'description': keyword.description, 'editUrl': reverse( EditStructuredKeywordView.urlname, args=[self.domain, keyword.couch_id] ) if keyword.is_structured_sms() else reverse( EditNormalKeywordView.urlname, args=[self.domain, keyword.couch_id] ), 'deleteModalId': 'delete-%s' % keyword.couch_id, } def _fmt_deleted_keyword_data(self, keyword): return { 'keyword': keyword.keyword, 'description': keyword.description, } def get_deleted_item_data(self, item_id): try: k = Keyword.objects.get(couch_id=item_id) except Keyword.DoesNotExist: raise Http404() if k.domain != self.domain: raise Http404() k.delete() return { 'itemData': self._fmt_deleted_keyword_data(k), 'template': 'keyword-deleted-template', } def post(self, *args, **kwargs): return self.paginate_crud_response
class AddAutomaticUpdateRuleView(JSONResponseMixin, DataInterfaceSection): template_name = 'data_interfaces/add_automatic_update_rule.html' urlname = 'add_automatic_update_rule' page_title = ugettext_lazy("Add Automatic Case Close Rule") @property def page_url(self): return reverse(self.urlname, args=[self.domain]) @property def initial_rule_form(self): return AddAutomaticCaseUpdateRuleForm( domain=self.domain, initial={ 'action': AddAutomaticCaseUpdateRuleForm.ACTION_CLOSE, 'property_value_type': AutomaticUpdateAction.EXACT, }) @property @memoized def rule_form(self): if self.request.method == 'POST': return AddAutomaticCaseUpdateRuleForm(self.request.POST, domain=self.domain) else: return self.initial_rule_form @property def page_context(self): return { 'form': self.rule_form, } @allow_remote_invocation def get_case_property_map(self): data = all_case_properties_by_domain(self.domain, include_parent_properties=False) return { 'data': data, 'success': True, } @use_angular_js @use_typeahead @method_decorator(requires_privilege_with_fallback(privileges.DATA_CLEANUP) ) def dispatch(self, *args, **kwargs): return super(AddAutomaticUpdateRuleView, self).dispatch(*args, **kwargs) def create_criteria(self, rule): for condition in self.rule_form.cleaned_data['conditions']: AutomaticUpdateRuleCriteria.objects.create( rule=rule, property_name=condition['property_name'], property_value=condition['property_value'], match_type=condition['property_match_type'], ) def create_actions(self, rule): if self.rule_form._closes_case(): AutomaticUpdateAction.objects.create( rule=rule, action=AutomaticUpdateAction.ACTION_CLOSE, ) if self.rule_form._updates_case(): AutomaticUpdateAction.objects.create( rule=rule, action=AutomaticUpdateAction.ACTION_UPDATE, property_name=self.rule_form. cleaned_data['update_property_name'], property_value=self.rule_form. cleaned_data['update_property_value'], property_value_type=self.rule_form. cleaned_data['property_value_type']) def create_rule(self): with transaction.atomic(): rule = AutomaticUpdateRule.objects.create( domain=self.domain, name=self.rule_form.cleaned_data['name'], case_type=self.rule_form.cleaned_data['case_type'], active=True, server_modified_boundary=self.rule_form. cleaned_data['server_modified_boundary'], filter_on_server_modified=self.rule_form. cleaned_data['filter_on_server_modified'], workflow=AutomaticUpdateRule.WORKFLOW_CASE_UPDATE, ) self.create_criteria(rule) self.create_actions(rule) def post(self, request, *args, **kwargs): if self.rule_form.is_valid(): self.create_rule() return HttpResponseRedirect( reverse(AutomaticUpdateRuleListView.urlname, args=[self.domain])) # We can't call self.get() because JSONResponseMixin gets confused # since we're processing a post request. So instead we have to call # .get() directly on super(JSONResponseMixin, self), which correctly # is DataInterfaceSection in this case return super(JSONResponseMixin, self).get(request, *args, **kwargs)
from __future__ import absolute_import from __future__ import unicode_literals from django.utils.decorators import method_decorator from corehq import privileges from corehq.apps.accounting.decorators import requires_privilege_with_fallback from corehq.apps.reports.dispatcher import ReportDispatcher, ProjectReportDispatcher, datespan_default from corehq.apps.users.decorators import require_permission from corehq.apps.users.models import Permissions from django_prbac.utils import has_privilege require_can_edit_data = require_permission(Permissions.edit_data) require_form_management_privilege = requires_privilege_with_fallback(privileges.DATA_CLEANUP) class EditDataInterfaceDispatcher(ReportDispatcher): prefix = 'edit_data_interface' map_name = 'EDIT_DATA_INTERFACES' @method_decorator(require_can_edit_data) @datespan_default def dispatch(self, request, *args, **kwargs): from corehq.apps.case_importer.base import ImportCases from .interfaces import BulkFormManagementInterface if kwargs['report_slug'] == ImportCases.slug: return self.bulk_import_case_dispatch(request, *args, **kwargs) elif (kwargs['report_slug'] == BulkFormManagementInterface.slug and not kwargs.get('skip_permissions_check')): return self.bulk_form_management_dispatch(request, *args, **kwargs)
class UploadCommCareUsers(BaseManageCommCareUserView): template_name = 'users/upload_commcare_users.html' urlname = 'upload_commcare_users' page_title = ugettext_noop("Bulk Upload Mobile Workers") @method_decorator( requires_privilege_with_fallback(privileges.BULK_USER_MANAGEMENT)) def dispatch(self, request, *args, **kwargs): return super(UploadCommCareUsers, self).dispatch(request, *args, **kwargs) @property def page_context(self): context = { 'bulk_upload': { "help_site": { "address": BULK_MOBILE_HELP_SITE, "name": _("CommCare Help Site"), }, "download_url": reverse("download_commcare_users", args=(self.domain, )), "adjective": _("mobile worker"), "plural_noun": _("mobile workers"), }, 'show_secret_settings': self.request.REQUEST.get("secret", False), } context.update({ 'bulk_upload_form': get_bulk_upload_form(context), }) return context def post(self, request, *args, **kwargs): upload = request.FILES.get('bulk_upload_file') """View's dispatch method automatically calls this""" try: self.workbook = WorkbookJSONReader(upload) except InvalidFileException: try: csv.DictReader( io.StringIO(upload.read().decode('ascii'), newline=None)) return HttpResponseBadRequest( "CommCare HQ no longer supports CSV upload. " "Please convert to Excel 2007 or higher (.xlsx) " "and try again.") except UnicodeDecodeError: return HttpResponseBadRequest("Unrecognized format") except JSONReaderError as e: messages.error(request, 'Your upload was unsuccessful. %s' % e.message) return self.get(request, *args, **kwargs) except HeaderValueError as e: return HttpResponseBadRequest( "Upload encountered a data type error: %s" % e.message) try: self.user_specs = self.workbook.get_worksheet(title='users') except WorksheetNotFound: try: self.user_specs = self.workbook.get_worksheet() except WorksheetNotFound: return HttpResponseBadRequest("Workbook has no worksheets") try: self.group_specs = self.workbook.get_worksheet(title='groups') except WorksheetNotFound: self.group_specs = [] self.location_specs = [] if Domain.get_by_name(self.domain).commtrack_enabled: try: self.location_specs = self.workbook.get_worksheet( title='locations') except WorksheetNotFound: # if there is no sheet for locations (since this was added # later and is optional) we don't error pass try: check_headers(self.user_specs) except UserUploadError as e: return HttpResponseBadRequest(e) task_ref = expose_download(None, expiry=1 * 60 * 60) task = bulk_upload_async.delay(self.domain, list(self.user_specs), list(self.group_specs), list(self.location_specs)) task_ref.set_task(task) return HttpResponseRedirect( reverse(UserUploadStatusView.urlname, args=[self.domain, task_ref.download_id]))
from corehq.apps.users.decorators import require_permission from corehq.apps.users.models import Permissions from dimagi.utils.decorators.memoized import memoized from .models import UI_SIMPLE_FIXED, UI_COMPLEX from .util import can_use_survey_reminders, get_form_list, get_form_name, get_recipient_name from corehq.apps.domain.models import Domain from corehq.util.timezones.utils import get_timezone_for_user from custom.ewsghana.forms import EWSBroadcastForm ACTION_ACTIVATE = 'activate' ACTION_DEACTIVATE = 'deactivate' ACTION_DELETE = 'delete' reminders_framework_permission = lambda *args, **kwargs: ( require_permission(Permissions.edit_data)( requires_privilege_with_fallback(privileges.REMINDERS_FRAMEWORK)(*args, **kwargs) ) ) survey_reminders_permission = lambda *args, **kwargs: ( require_permission(Permissions.edit_data)( requires_privilege_with_fallback(privileges.INBOUND_SMS)(*args, **kwargs) ) ) def get_project_time_info(domain): timezone = get_timezone_for_user(None, domain) now = pytz.utc.localize(datetime.utcnow()) timezone_now = now.astimezone(timezone) return (timezone, now, timezone_now)
from corehq.apps.users.decorators import require_permission from corehq.apps.users.models import Permissions from dimagi.utils.decorators.memoized import memoized from .models import UI_SIMPLE_FIXED, UI_COMPLEX from .util import can_use_survey_reminders, get_form_list, get_form_name, get_recipient_name from corehq.apps.domain.models import Domain from corehq.util.timezones.utils import get_timezone_for_user from custom.ewsghana.forms import EWSBroadcastForm ACTION_ACTIVATE = "activate" ACTION_DEACTIVATE = "deactivate" ACTION_DELETE = "delete" reminders_framework_permission = lambda *args, **kwargs: ( require_permission(Permissions.edit_data)( requires_privilege_with_fallback(privileges.REMINDERS_FRAMEWORK)(*args, **kwargs) ) ) survey_reminders_permission = lambda *args, **kwargs: ( require_permission(Permissions.edit_data)(requires_privilege_with_fallback(privileges.INBOUND_SMS)(*args, **kwargs)) ) def get_project_time_info(domain): timezone = get_timezone_for_user(None, domain) now = pytz.utc.localize(datetime.utcnow()) timezone_now = now.astimezone(timezone) return (timezone, now, timezone_now)