Ejemplo n.º 1
0
def _sync_user_phone_numbers(couch_user_id):
    couch_user = CouchUser.get_by_user_id(couch_user_id)

    if not isinstance(couch_user, CommCareUser):
        # It isn't necessary to sync WebUser's phone numbers right now
        # and we need to think through how to support entries when a user
        # can belong to multiple domains
        return

    with CriticalSection([couch_user.phone_sync_key], timeout=5 * 60):
        phone_entries = couch_user.get_phone_entries()

        if (couch_user.is_deleted() or
            (not couch_user.is_active and
             not USE_SMS_WITH_INACTIVE_CONTACTS.enabled(couch_user.domain))):
            for phone_number in phone_entries.values():
                phone_number.delete()
            return

        numbers_that_should_exist = [
            apply_leniency(phone_number)
            for phone_number in couch_user.phone_numbers
        ]

        # Delete entries that should not exist
        for phone_number in phone_entries.keys():
            if phone_number not in numbers_that_should_exist:
                phone_entries[phone_number].delete()

        # Create entries that should exist but do not exist
        for phone_number in numbers_that_should_exist:
            if phone_number not in phone_entries:
                try:
                    couch_user.create_phone_entry(phone_number)
                except InvalidFormatException:
                    pass
Ejemplo n.º 2
0
    def post(self, request, *args, **kwargs):
        # request.body already confirmed to be a valid json dict in ZiplineOrderStatusView
        data = json.loads(request.body)

        try:
            self.validate_and_clean_int(data, 'orderId')
            self.validate_and_clean_timestamp(data, 'timestamp')
        except OrderStatusValidationError as e:
            return get_error_response(e.error_message)

        order_id = data['orderId']

        with CriticalSection([get_order_update_critical_section_key(order_id)
                              ]), transaction.atomic():
            try:
                order = EmergencyOrder.objects.get(pk=order_id)
            except EmergencyOrder.DoesNotExist:
                return get_error_response('Order not found')

            if order.domain != self.domain:
                return get_error_response('Order not found')

            try:
                self.validate_and_clean_payload(order, data)
            except OrderStatusValidationError as e:
                return get_error_response(e.error_message)

            send_sms_response, response_dict = self.process_status_update(
                order, data)

        if send_sms_response:
            # This should be done outside of the transaction to prevent issues with QueuedSMS pk's
            # being available immediately.
            self.send_sms_for_status_update(order, data)

        return JsonResponse(response_dict)
Ejemplo n.º 3
0
 def post(self, request, *args, **kwargs):
     self.request_form = DomainRequestForm(request.POST)
     if self.request_form.is_valid():
         data = self.request_form.cleaned_data
         with CriticalSection(["domain_request_%s" % data['domain']]):
             if DomainRequest.by_email(data['domain'],
                                       data['email']) is not None:
                 messages.error(
                     request,
                     _("A request is pending for this email. "
                       "You will receive an email when the request is approved."
                       ))
             else:
                 domain_request = DomainRequest(**data)
                 domain_request.send_request_email()
                 domain_request.save()
                 domain_obj = Domain.get_by_name(domain_request.domain)
                 return render(
                     request, "users/confirmation_sent.html", {
                         'hr_name':
                         domain_obj.display_name()
                         if domain_obj else domain_request.domain,
                     })
     return self.get(request, *args, **kwargs)
Ejemplo n.º 4
0
def copy_snapshot(request, domain):
    user = request.couch_user
    if not user.is_eula_signed():
        messages.error(request,
                       'You must agree to our eula to download an app')
        return project_info(request, domain)

    dom = Domain.get(domain)
    if request.method == "POST" and dom.is_snapshot:
        assert dom.full_applications(
            include_builds=False
        ), 'Bad attempt to copy project without any apps!'

        from corehq.apps.registration.forms import DomainRegistrationForm

        args = {
            'domain_name': request.POST['new_project_name'],
            'hr_name': request.POST['new_project_name'],
            'eula_confirmed': True,
        }
        form = DomainRegistrationForm(args)

        if request.POST.get('new_project_name', ""):
            if not dom.published:
                messages.error(
                    request,
                    _("This project is not published and can't be downloaded"))
                return project_info(request, domain)

            if not form.is_valid():
                messages.error(request, form.errors)
                return project_info(request, domain)

            new_domain_name = form.cleaned_data['hr_name']
            with CriticalSection([
                    'copy_domain_snapshot_{}_to_{}'.format(
                        dom.name, new_domain_name)
            ]):
                try:
                    new_domain = dom.save_copy(
                        new_domain_name,
                        new_hr_name=form.cleaned_data['hr_name'],
                        user=user)
                except NameUnavailableException:
                    messages.error(request,
                                   _("A project by that name already exists"))
                    return project_info(request, domain)

                # sign new project up for trial
                create_30_day_trial(new_domain)

            def inc_downloads(d):
                d.downloads += 1

            apply_update(dom, inc_downloads)
            messages.success(request,
                             render_to_string(
                                 "appstore/partials/view_wiki.html",
                                 {"pre": _("Project copied successfully!")}),
                             extra_tags="html")
            return HttpResponseRedirect(
                reverse('view_app',
                        args=[
                            new_domain.name,
                            new_domain.full_applications()[0].get_id
                        ]))
        else:
            messages.error(request,
                           _("You must specify a name for the new project"))
            return project_info(request, domain)
    else:
        return HttpResponseRedirect(reverse('project_info', args=[domain]))
Ejemplo n.º 5
0
def build_async_indicators(indicator_doc_ids):
    # written to be used with _queue_indicators, indicator_doc_ids must
    #   be a chunk of 100
    memoizers = {'configs': {}, 'adapters': {}}
    assert(len(indicator_doc_ids)) <= ASYNC_INDICATOR_CHUNK_SIZE

    def handle_exception(exception, config_id, doc, adapter):
        metric = None
        if isinstance(exception, (ProtocolError, ReadTimeout)):
            metric = 'commcare.async_indicator.riak_error'
        elif isinstance(exception, (ESError, ConnectionTimeout)):
            # a database had an issue so log it and go on to the next document
            metric = 'commcare.async_indicator.es_error'
        elif isinstance(exception, (DatabaseError, InternalError)):
            # a database had an issue so log it and go on to the next document
            metric = 'commcare.async_indicator.psql_error'
        else:
            # getting the config could fail before the adapter is set
            if adapter:
                adapter.handle_exception(doc, exception)
        if metric:
            metrics_counter(metric, tags={'config_id': config_id})

    def doc_ids_from_rows(rows):
        formatted_rows = [
            {column.column.database_column_name.decode('utf-8'): column.value for column in row}
            for row in rows
        ]
        return set(row['doc_id'] for row in formatted_rows)

    def _get_config(config_id):
        config_by_id = memoizers['configs']
        if config_id in config_by_id:
            return config_by_id[config_id]
        else:
            config = _get_config_by_id(config_id)
            config_by_id[config_id] = config
            return config

    def _get_adapter(config):
        adapter_by_config = memoizers['adapters']
        if config._id in adapter_by_config:
            return adapter_by_config[config._id]
        else:
            adapter = get_indicator_adapter(config, load_source='build_async_indicators')
            adapter_by_config[config._id] = adapter
            return adapter

    def _metrics_timer(step, config_id=None):
        tags = {
            'action': step,
        }
        if config_id and settings.ENTERPRISE_MODE:
            tags['config_id'] = config_id
        else:
            # Prometheus requires consistent tags even if not available
            tags['config_id'] = None
        return metrics_histogram_timer(
            'commcare.async_indicator.timing',
            timing_buckets=(.03, .1, .3, 1, 3, 10), tags=tags
        )

    # tracks processed/deleted configs to be removed from each indicator
    configs_to_remove_by_indicator_id = defaultdict(list)

    def _mark_config_to_remove(config_id, indicator_ids):
        for _id in indicator_ids:
            configs_to_remove_by_indicator_id[_id].append(config_id)

    timer = TimingContext()
    lock_keys = [
        get_async_indicator_modify_lock_key(indicator_doc_id)
        for indicator_doc_id in indicator_doc_ids
    ]
    with CriticalSection(lock_keys):
        all_indicators = AsyncIndicator.objects.filter(
            doc_id__in=indicator_doc_ids
        )
        if not all_indicators:
            return

        doc_store = get_document_store_for_doc_type(
            all_indicators[0].domain, all_indicators[0].doc_type,
            load_source="build_async_indicators",
        )
        failed_indicators = set()

        rows_to_save_by_adapter = defaultdict(list)
        docs_to_delete_by_adapter = defaultdict(list)
        # there will always be one AsyncIndicator per doc id
        indicator_by_doc_id = {i.doc_id: i for i in all_indicators}
        config_ids = set()
        with timer:
            for doc in doc_store.iter_documents(list(indicator_by_doc_id.keys())):
                indicator = indicator_by_doc_id[doc['_id']]
                eval_context = EvaluationContext(doc)
                for config_id in indicator.indicator_config_ids:
                    with _metrics_timer('transform', config_id):
                        config_ids.add(config_id)
                        try:
                            config = _get_config(config_id)
                        except (ResourceNotFound, StaticDataSourceConfigurationNotFoundError):
                            celery_task_logger.info("{} no longer exists, skipping".format(config_id))
                            # remove because the config no longer exists
                            _mark_config_to_remove(config_id, [indicator.pk])
                            continue
                        except ESError:
                            celery_task_logger.info("ES errored when trying to retrieve config")
                            failed_indicators.add(indicator)
                            continue
                        adapter = None
                        try:
                            adapter = _get_adapter(config)
                            rows_to_save = adapter.get_all_values(doc, eval_context)
                            if rows_to_save:
                                rows_to_save_by_adapter[adapter].extend(rows_to_save)
                            else:
                                docs_to_delete_by_adapter[adapter].append(doc)
                            eval_context.reset_iteration()
                        except Exception as e:
                            failed_indicators.add(indicator)
                            handle_exception(e, config_id, doc, adapter)

            with _metrics_timer('single_batch_update'):
                for adapter, rows in rows_to_save_by_adapter.items():
                    doc_ids = doc_ids_from_rows(rows)
                    indicators = [indicator_by_doc_id[doc_id] for doc_id in doc_ids]
                    try:
                        with _metrics_timer('update', adapter.config._id):
                            adapter.save_rows(rows, use_shard_col=True)
                    except Exception as e:
                        failed_indicators.union(indicators)
                        message = str(e)
                        notify_exception(None, "Exception bulk saving async indicators:{}".format(message))
                    else:
                        # remove because it's successfully processed
                        _mark_config_to_remove(
                            config_id,
                            [i.pk for i in indicators]
                        )

            with _metrics_timer('single_batch_delete'):
                for adapter, docs in docs_to_delete_by_adapter.items():
                    with _metrics_timer('delete', adapter.config._id):
                        adapter.bulk_delete(docs)

        # delete fully processed indicators
        processed_indicators = set(all_indicators) - failed_indicators
        AsyncIndicator.objects.filter(pk__in=[i.pk for i in processed_indicators]).delete()

        # update failure for failed indicators
        with transaction.atomic():
            for indicator in failed_indicators:
                indicator.update_failure(
                    configs_to_remove_by_indicator_id.get(indicator.pk, [])
                )
                indicator.save()

        metrics_counter('commcare.async_indicator.processed_success', len(processed_indicators))
        metrics_counter('commcare.async_indicator.processed_fail', len(failed_indicators))
        metrics_counter(
            'commcare.async_indicator.processing_time', timer.duration,
            tags={'config_ids': config_ids}
        )
        metrics_counter(
            'commcare.async_indicator.processed_total', len(indicator_doc_ids),
            tags={'config_ids': config_ids}
        )
Ejemplo n.º 6
0
def request_new_domain(request, form, is_new_user=True):
    now = datetime.utcnow()
    current_user = CouchUser.from_django_user(request.user)

    dom_req = RegistrationRequest()
    if is_new_user:
        dom_req.request_time = now
        dom_req.request_ip = get_ip(request)
        dom_req.activation_guid = uuid.uuid1().hex

    project_name = form.cleaned_data.get('hr_name') or form.cleaned_data.get(
        'project_name')
    name = name_to_url(project_name, "project")
    with CriticalSection(['request_domain_name_{}'.format(name)]):
        name = Domain.generate_name(name)
        new_domain = Domain(name=name,
                            hr_name=project_name,
                            is_active=False,
                            date_created=datetime.utcnow(),
                            creating_user=current_user.username,
                            secure_submissions=True,
                            use_sql_backend=True,
                            first_domain_for_user=is_new_user)

        if form.cleaned_data.get('domain_timezone'):
            new_domain.default_timezone = form.cleaned_data['domain_timezone']

        if not is_new_user:
            new_domain.is_active = True

        # ensure no duplicate domain documents get created on cloudant
        new_domain.save(**get_safe_write_kwargs())

    if not new_domain.name:
        new_domain.name = new_domain._id
        new_domain.save()  # we need to get the name from the _id

    if is_new_user:
        # Only new-user domains are eligible for Advanced trial
        # domains with no subscription are equivalent to be on free Community plan
        create_30_day_advanced_trial(new_domain, current_user.username)
    else:
        ensure_explicit_community_subscription(new_domain.name, date.today())

    UserRole.init_domain_with_presets(new_domain.name)

    # add user's email as contact email for billing account for the domain
    account = BillingAccount.get_account_by_domain(new_domain.name)
    billing_contact, _ = BillingContactInfo.objects.get_or_create(
        account=account)
    billing_contact.email_list = [current_user.email]
    billing_contact.save()

    dom_req.domain = new_domain.name

    if request.user.is_authenticated():
        if not current_user:
            current_user = WebUser()
            current_user.sync_from_django_user(request.user)
            current_user.save()
        current_user.add_domain_membership(new_domain.name, is_admin=True)
        current_user.save()
        dom_req.requesting_user_username = request.user.username
        dom_req.new_user_username = request.user.username

    if is_new_user:
        dom_req.save()
        send_domain_registration_email(request.user.email, dom_req.domain,
                                       dom_req.activation_guid,
                                       request.user.get_full_name())
    send_new_request_update_email(request.user,
                                  get_ip(request),
                                  new_domain.name,
                                  is_new_user=is_new_user)

    meta = get_meta(request)
    track_created_new_project_space_on_hubspot.delay(current_user,
                                                     request.COOKIES, meta)
    return new_domain.name
Ejemplo n.º 7
0
def _build_async_indicators(indicator_doc_ids):
    def handle_exception(exception, config_id, doc, adapter):
        metric = None
        if isinstance(exception, (ProtocolError, ReadTimeout)):
            metric = 'commcare.async_indicator.riak_error'
        elif isinstance(exception, (ESError, ConnectionTimeout)):
            # a database had an issue so log it and go on to the next document
            metric = 'commcare.async_indicator.es_error'
        elif isinstance(exception, (DatabaseError, InternalError)):
            # a database had an issue so log it and go on to the next document
            metric = 'commcare.async_indicator.psql_error'
        else:
            # getting the config could fail before the adapter is set
            if adapter:
                adapter.handle_exception(doc, exception)
        if metric:
            datadog_counter(metric,
                            1,
                            tags={
                                'config_id': config_id,
                                'doc_id': doc['_id']
                            })

    def doc_ids_from_rows(rows):
        formatted_rows = [{
            column.column.database_column_name.decode('utf-8'): column.value
            for column in row
        } for row in rows]
        return set(row['doc_id'] for row in formatted_rows)

    # tracks processed/deleted configs to be removed from each indicator
    configs_to_remove_by_indicator_id = defaultdict(list)

    def _mark_config_to_remove(config_id, indicator_ids):
        for _id in indicator_ids:
            configs_to_remove_by_indicator_id[_id].append(config_id)

    timer = TimingContext()
    lock_keys = [
        get_async_indicator_modify_lock_key(indicator_id)
        for indicator_id in indicator_doc_ids
    ]
    with CriticalSection(lock_keys):
        all_indicators = AsyncIndicator.objects.filter(
            doc_id__in=indicator_doc_ids)
        if not all_indicators:
            return

        doc_store = get_document_store_for_doc_type(all_indicators[0].domain,
                                                    all_indicators[0].doc_type)
        failed_indicators = set()

        rows_to_save_by_adapter = defaultdict(list)
        indicator_by_doc_id = {i.doc_id: i for i in all_indicators}
        config_ids = set()
        with timer:
            for doc in doc_store.iter_documents(
                    list(indicator_by_doc_id.keys())):
                indicator = indicator_by_doc_id[doc['_id']]
                eval_context = EvaluationContext(doc)
                for config_id in indicator.indicator_config_ids:
                    config_ids.add(config_id)
                    try:
                        config = _get_config_by_id(config_id)
                    except (ResourceNotFound,
                            StaticDataSourceConfigurationNotFoundError):
                        celery_task_logger.info(
                            "{} no longer exists, skipping".format(config_id))
                        # remove because the config no longer exists
                        _mark_config_to_remove(config_id, [indicator.pk])
                        continue
                    except ESError:
                        celery_task_logger.info(
                            "ES errored when trying to retrieve config")
                        failed_indicators.add(indicator)
                        continue
                    adapter = None
                    try:
                        adapter = get_indicator_adapter(config)
                        rows_to_save_by_adapter[adapter].extend(
                            adapter.get_all_values(doc, eval_context))
                        eval_context.reset_iteration()
                    except Exception as e:
                        failed_indicators.add(indicator)
                        handle_exception(e, config_id, doc, adapter)

            for adapter, rows in six.iteritems(rows_to_save_by_adapter):
                doc_ids = doc_ids_from_rows(rows)
                indicators = [
                    indicator_by_doc_id[doc_id] for doc_id in doc_ids
                ]
                try:
                    adapter.save_rows(rows)
                except Exception as e:
                    failed_indicators.union(indicators)
                    message = six.text_type(e)
                    notify_exception(
                        None,
                        "Exception bulk saving async indicators:{}".format(
                            message))
                else:
                    # remove because it's sucessfully processed
                    _mark_config_to_remove(config_id,
                                           [i.pk for i in indicators])

        # delete fully processed indicators
        processed_indicators = set(all_indicators) - failed_indicators
        AsyncIndicator.objects.filter(
            pk__in=[i.pk for i in processed_indicators]).delete()

        # update failure for failed indicators
        with transaction.atomic():
            for indicator in failed_indicators:
                indicator.update_failure(
                    configs_to_remove_by_indicator_id.get(indicator.pk, []))
                indicator.save()

        datadog_counter('commcare.async_indicator.processed_success',
                        len(processed_indicators))
        datadog_counter('commcare.async_indicator.processed_fail',
                        len(failed_indicators))
        datadog_histogram('commcare.async_indicator.processing_time',
                          timer.duration / len(indicator_doc_ids),
                          tags=[
                              'config_ids:{}'.format(config_ids),
                          ])
Ejemplo n.º 8
0
def save_document(doc_ids):
    lock_keys = []
    for doc_id in doc_ids:
        lock_keys.append(get_async_indicator_modify_lock_key(doc_id))

    indicator_config_ids = None
    timer = TimingContext()
    with CriticalSection(lock_keys):
        indicators = AsyncIndicator.objects.filter(doc_id__in=doc_ids)
        if not indicators:
            return

        first_indicator = indicators[0]
        processed_indicators = []
        failed_indicators = []

        for i in indicators:
            assert i.domain == first_indicator.domain
            assert i.doc_type == first_indicator.doc_type

        indicator_by_doc_id = {i.doc_id: i for i in indicators}
        doc_store = get_document_store(first_indicator.domain,
                                       first_indicator.doc_type)
        indicator_config_ids = first_indicator.indicator_config_ids
        related_docs_to_rebuild = set()

        with timer:
            for doc in doc_store.iter_documents(
                    list(indicator_by_doc_id.keys())):
                indicator = indicator_by_doc_id[doc['_id']]
                successfully_processed, to_remove, rebuild_related_docs = _save_document_helper(
                    indicator, doc)
                if rebuild_related_docs:
                    related_docs_to_rebuild = related_docs_to_rebuild.union(
                        icds_get_related_docs_ids(doc['_id']))
                if successfully_processed:
                    processed_indicators.append(indicator.pk)
                else:
                    failed_indicators.append((indicator, to_remove))

        num_processed = len(processed_indicators)
        num_failed = len(failed_indicators)
        AsyncIndicator.objects.filter(pk__in=processed_indicators).delete()
        with transaction.atomic():
            for indicator, to_remove in failed_indicators:
                indicator.update_failure(to_remove)
                indicator.save()

    # remove any related docs that were just rebuilt
    related_docs_to_rebuild = related_docs_to_rebuild - set(doc_ids)
    # queue the docs that aren't already queued
    _queue_indicators(
        AsyncIndicator.objects.filter(doc_id__in=related_docs_to_rebuild,
                                      date_queued=None))

    datadog_counter('commcare.async_indicator.processed_success',
                    num_processed)
    datadog_counter('commcare.async_indicator.processed_fail', num_failed)
    datadog_histogram('commcare.async_indicator.processing_time',
                      timer.duration,
                      tags=['config_ids:{}'.format(indicator_config_ids)])
Ejemplo n.º 9
0
def sync_case_for_messaging_rule(self, domain, case_id, rule_id):
    try:
        with CriticalSection([get_sync_key(case_id)], timeout=5 * 60):
            _sync_case_for_messaging_rule(domain, case_id, rule_id)
    except Exception as e:
        self.retry(exc=e)
Ejemplo n.º 10
0
def critical_section_for_smsforms_sessions(contact_id):
    return CriticalSection(
        ['smsforms-sessions-lock-for-contact-%s' % contact_id], timeout=5 * 60)
Ejemplo n.º 11
0
def sync_usercase(user):
    with CriticalSection(get_sync_lock_key(user._id)):
        _UserCaseHelper.commit(list(_iter_sync_usercase_helpers(user)))
Ejemplo n.º 12
0
def request_new_domain(request, form, org, domain_type=None, new_user=True):
    now = datetime.utcnow()
    current_user = CouchUser.from_django_user(request.user)

    dom_req = RegistrationRequest()
    if new_user:
        dom_req.request_time = now
        dom_req.request_ip = get_ip(request)
        dom_req.activation_guid = uuid.uuid1().hex

    name = form.cleaned_data['hr_name']
    with CriticalSection(['request_domain_name_{}'.format(name)]):
        name = Domain.generate_name(name)
        new_domain = Domain(
            name=name,
            hr_name=form.cleaned_data['hr_name'],
            is_active=False,
            date_created=datetime.utcnow(),
            creating_user=current_user.username,
            secure_submissions=True,
        )

        if form.cleaned_data.get('domain_timezone'):
            new_domain.default_timezone = form.cleaned_data['domain_timezone']

        if org:
            new_domain.organization = org
            new_domain.hr_name = request.POST.get('domain_hrname',
                                                  None) or new_domain.name

        if not new_user:
            new_domain.is_active = True

        # ensure no duplicate domain documents get created on cloudant
        new_domain.save(**get_safe_write_kwargs())

    if domain_type == 'commtrack':
        new_domain.convert_to_commtrack()

    if not new_domain.name:
        new_domain.name = new_domain._id
        new_domain.save()  # we need to get the name from the _id

    create_30_day_trial(new_domain)

    dom_req.domain = new_domain.name

    if request.user.is_authenticated():
        if not current_user:
            current_user = WebUser()
            current_user.sync_from_django_user(request.user)
            current_user.save()
        current_user.add_domain_membership(new_domain.name, is_admin=True)
        current_user.save()
        dom_req.requesting_user_username = request.user.username
        dom_req.new_user_username = request.user.username

    if new_user:
        dom_req.save()
        send_domain_registration_email(request.user.email, dom_req.domain,
                                       dom_req.activation_guid)
    else:
        send_global_domain_registration_email(request.user, new_domain.name)
    send_new_request_update_email(request.user,
                                  get_ip(request),
                                  new_domain.name,
                                  is_new_user=new_user)

    return new_domain.name
Ejemplo n.º 13
0
def request_new_domain(request, form, is_new_user=True):
    now = datetime.utcnow()
    current_user = CouchUser.from_django_user(request.user, strict=True)

    dom_req = RegistrationRequest()
    if is_new_user:
        dom_req.request_time = now
        dom_req.request_ip = get_ip(request)
        dom_req.activation_guid = uuid.uuid1().hex

    project_name = form.cleaned_data.get('hr_name') or form.cleaned_data.get(
        'project_name')
    name = name_to_url(project_name, "project")
    with CriticalSection(['request_domain_name_{}'.format(name)]):
        name = Domain.generate_name(name)
        new_domain = Domain(name=name,
                            hr_name=project_name,
                            is_active=False,
                            date_created=datetime.utcnow(),
                            creating_user=current_user.username,
                            secure_submissions=True,
                            use_sql_backend=True,
                            first_domain_for_user=is_new_user)

        # Avoid projects created by dimagi.com staff members as self started
        new_domain.internal.self_started = not current_user.is_dimagi

        if form.cleaned_data.get('domain_timezone'):
            new_domain.default_timezone = form.cleaned_data['domain_timezone']

        if not is_new_user:
            new_domain.is_active = True

        # ensure no duplicate domain documents get created on cloudant
        new_domain.save(**get_safe_write_kwargs())

    if not new_domain.name:
        new_domain.name = new_domain._id
        new_domain.save()  # we need to get the name from the _id

    with transaction.atomic():
        if is_new_user:
            # Only new-user domains are eligible for Advanced trial
            # domains with no subscription are equivalent to be on free Community plan
            create_30_day_advanced_trial(new_domain, current_user.username)
        else:
            ensure_explicit_community_subscription(
                new_domain.name,
                date.today(),
                SubscriptionAdjustmentMethod.USER,
                web_user=current_user.username,
            )

    UserRole.init_domain_with_presets(new_domain.name)

    # add user's email as contact email for billing account for the domain
    account = BillingAccount.get_account_by_domain(new_domain.name)
    billing_contact, _ = BillingContactInfo.objects.get_or_create(
        account=account)
    billing_contact.email_list = [current_user.email]
    billing_contact.save()

    dom_req.domain = new_domain.name

    if request.user.is_authenticated:
        if not current_user:
            current_user = WebUser()
            current_user.sync_from_django_user(request.user)
            current_user.save()
        current_user.add_domain_membership(new_domain.name, is_admin=True)
        current_user.save()
        dom_req.requesting_user_username = request.user.username
        dom_req.new_user_username = request.user.username

    if is_new_user:
        dom_req.save()
        if settings.IS_SAAS_ENVIRONMENT:
            #  Load template apps to the user's new domain in parallel
            from corehq.apps.app_manager.tasks import load_appcues_template_app
            header = [
                load_appcues_template_app.si(new_domain.name,
                                             current_user.username, slug)
                for slug in APPCUES_APP_SLUGS
            ]
            callback = send_domain_registration_email.si(
                request.user.email, dom_req.domain, dom_req.activation_guid,
                request.user.get_full_name(), request.user.first_name)
            chord(header)(callback)
        else:
            send_domain_registration_email(request.user.email, dom_req.domain,
                                           dom_req.activation_guid,
                                           request.user.get_full_name(),
                                           request.user.first_name)
    send_new_request_update_email(request.user,
                                  get_ip(request),
                                  new_domain.name,
                                  is_new_user=is_new_user)

    send_hubspot_form(HUBSPOT_CREATED_NEW_PROJECT_SPACE_FORM_ID, request)
    return new_domain.name
Ejemplo n.º 14
0
def sync_user_case(commcare_user, case_type, owner_id, copy_user_data=True):
    """
    Each time a CommCareUser is saved this method gets called and creates or updates
    a case associated with the user with the user's details.

    This is also called to create user cases when the usercase is used for the
    first time.
    """
    with CriticalSection(
        ['user_case_%s_for_%s' % (case_type, commcare_user._id)]):
        domain = commcare_user.project

        def valid_element_name(name):
            try:
                ElementTree.fromstring('<{}/>'.format(name))
                return True
            except ElementTree.ParseError:
                return False

        # remove any keys that aren't valid XML element names
        fields = {
            k: v
            for k, v in commcare_user.user_data.items()
            if valid_element_name(k)
        } if copy_user_data else {}

        # language or phone_number can be null and will break
        # case submission
        fields.update({
            'name': commcare_user.name or commcare_user.raw_username,
            'username': commcare_user.raw_username,
            'email': commcare_user.email,
            'language': commcare_user.language or '',
            'phone_number': commcare_user.phone_number or ''
        })

        case = get_case_by_domain_hq_user_id(domain.name, commcare_user._id,
                                             case_type)
        close = commcare_user.to_be_deleted() or not commcare_user.is_active
        caseblock = None
        if case:
            props = dict(case.dynamic_case_properties())

            changed = close != case.closed
            changed = changed or case.type != case_type
            changed = changed or case.name != fields['name']

            if not changed:
                for field, value in fields.items():
                    if field != 'name' and props.get(field) != value:
                        changed = True
                        break

            if changed:
                caseblock = CaseBlock(create=False,
                                      case_id=case._id,
                                      version=V2,
                                      case_type=case_type,
                                      close=close,
                                      update=fields)
        else:
            fields['hq_user_id'] = commcare_user._id
            caseblock = CaseBlock(create=True,
                                  case_id=uuid.uuid4().hex,
                                  owner_id=owner_id,
                                  user_id=owner_id,
                                  version=V2,
                                  case_type=case_type,
                                  update=fields)

        if caseblock:
            casexml = ElementTree.tostring(caseblock.as_xml())
            submit_case_blocks(casexml, domain.name)
Ejemplo n.º 15
0
def get_conditional_alert_edit_critical_section(rule_id):
    return CriticalSection(['edit-conditional-alert-%s' % rule_id],
                           timeout=5 * 60)
Ejemplo n.º 16
0
def migrate_domain(domain, dryrun=False, force_convert_columns=False):
    from couchexport.models import SavedExportSchema
    export_count = stale_get_export_count(domain)
    metas = []
    if export_count:
        for old_export in with_progress_bar(get_exports_json(domain),
                                            length=export_count,
                                            prefix=domain):
            with CriticalSection(['saved-export-{}'.format(old_export['_id'])],
                                 timeout=120):
                try:
                    _, migration_meta = convert_saved_export_to_export_instance(
                        domain,
                        SavedExportSchema.get(old_export['_id']),
                        dryrun=dryrun,
                        force_convert_columns=force_convert_columns,
                    )
                except Exception as e:
                    print('Failed parsing {}: {}'.format(old_export['_id'], e))
                    raise
                else:
                    metas.append(migration_meta)

    if not dryrun:
        set_toggle(OLD_EXPORTS.slug, domain, False, namespace=NAMESPACE_DOMAIN)
        toggle_js_domain_cachebuster.clear(domain)

    # Remote app migrations must have access to UserDefined columns and tables
    if any(map(lambda meta: meta.is_remote_app_migration, metas)):
        set_toggle(ALLOW_USER_DEFINED_EXPORT_COLUMNS.slug,
                   domain,
                   True,
                   namespace=NAMESPACE_DOMAIN)
        toggle_js_domain_cachebuster.clear(domain)

    for meta in metas:
        if not meta.skipped_tables and not meta.skipped_columns:
            continue

        output = '* Export information for export: {} *'.format(
            meta.old_export_url)
        schema_id_output = 'Generated schema: {}'.format(
            meta.generated_schema_id)
        print('')
        print('*' * len(output))
        print(output)
        print('* {}{} *'.format(
            schema_id_output, ' ' * (len(output) - len(schema_id_output) - 4)))
        print('*' * len(output))
        print('')

        if meta.skipped_tables:
            print('# Skipped tables #')
            for table_meta in meta.skipped_tables:
                table_meta.pretty_print()

        if meta.skipped_columns:
            print('# Skipped columns #')
            for column_meta in meta.skipped_columns:
                column_meta.pretty_print()
    return metas
Ejemplo n.º 17
0
def update_group_membership(request, domain, group_id):
    with CriticalSection(['update-group-membership-%s' % group_id]):
        return _update_group_membership(request, domain, group_id)
Ejemplo n.º 18
0
 def get_or_create_phone_entry(self, phone_number):
     with CriticalSection([self.phone_sync_key]):
         return self._get_or_create_phone_entry(phone_number)
Ejemplo n.º 19
0
 def _critical_section(channel):
     return CriticalSection([
         f'XFormsSessionSynchronization.critical_section.{channel.backend_id}/{channel.phone_number}'
     ], timeout=5 * 60)
Ejemplo n.º 20
0
 def get_or_create_api_key(self):
     if not self.api_key:
         with CriticalSection(['get-or-create-api-key-for-%d' % self.request.user.id]):
             api_key, _ = ApiKey.objects.get_or_create(user=self.request.user)
         self.api_key = api_key.key
     return self.api_key
Ejemplo n.º 21
0
    if cached_fields != new_field_set:
        cache.set(cache_key, new_field_set)
        return True

    return False


def _sync_user_case(commcare_user, case_type, owner_id, case=None):
    """
    Each time a CommCareUser is saved this method gets called and creates or updates
    a case associated with the user with the user's details.

    This is also called to create user cases when the usercase is used for the
    first time.
    """
    with CriticalSection(
        ['user_case_%s_for_%s' % (case_type, commcare_user._id)]):
        domain = commcare_user.domain
        fields = _get_user_case_fields(commcare_user, case_type, owner_id)
        case = case or CaseAccessors(domain).get_case_by_domain_hq_user_id(
            commcare_user._id, case_type)
        close = commcare_user.to_be_deleted() or not commcare_user.is_active
        user_case_helper = _UserCaseHelper(domain, owner_id)

        def case_should_be_reopened(case, user_case_should_be_closed):
            return case and case.closed and not user_case_should_be_closed

        if not case:
            user_case_helper.create_user_case(commcare_user, fields)
        else:
            if case_should_be_reopened(case, close):
                user_case_helper.re_open_case(case)
Ejemplo n.º 22
0
def update_group_membership(request, domain, group_id):
    if not (request.couch_user.can_edit_users_in_groups()
            or request.couch_user.can_edit_commcare_users()):
        return HttpResponseForbidden()
    with CriticalSection(['update-group-membership-%s' % group_id]):
        return _update_group_membership(request, domain, group_id)
Ejemplo n.º 23
0
def copy_snapshot(request, snapshot):
    user = request.couch_user
    if not user.is_eula_signed():
        messages.error(
            request,
            'You must agree to our terms of service to download an app')
        return HttpResponseRedirect(
            reverse(ProjectInformationView.urlname, args=[snapshot]))

    dom = Domain.get(snapshot)
    if request.method == "POST" and dom.is_snapshot:
        assert dom.full_applications(
            include_builds=False
        ), 'Bad attempt to copy project without any apps!'

        from corehq.apps.registration.forms import DomainRegistrationForm

        args = {
            'domain_name': request.POST['new_project_name'],
            'hr_name': request.POST['new_project_name'],
            'eula_confirmed': True,
        }
        form = DomainRegistrationForm(args)

        if request.POST.get('new_project_name', ""):
            if not dom.published:
                messages.error(
                    request,
                    _("This project is not published and can't be downloaded"))
                return HttpResponseRedirect(
                    reverse(ProjectInformationView.urlname, args=[snapshot]))

            if not form.is_valid():
                messages.error(request, form.errors)
                return HttpResponseRedirect(
                    reverse(ProjectInformationView.urlname, args=[snapshot]))

            new_domain_name = name_to_url(form.cleaned_data['hr_name'],
                                          "project")
            with CriticalSection([
                    'copy_domain_snapshot_{}_to_{}'.format(
                        dom.name, new_domain_name)
            ]):
                try:
                    new_domain = dom.save_copy(
                        new_domain_name,
                        new_hr_name=form.cleaned_data['hr_name'],
                        user=user)
                    if new_domain.commtrack_enabled:
                        new_domain.convert_to_commtrack()
                    ensure_explicit_community_subscription(
                        new_domain.name,
                        date.today(),
                        SubscriptionAdjustmentMethod.USER,
                        web_user=user.username,
                    )
                except NameUnavailableException:
                    messages.error(request,
                                   _("A project by that name already exists"))
                    return HttpResponseRedirect(
                        reverse(ProjectInformationView.urlname,
                                args=[snapshot]))

            def inc_downloads(d):
                d.downloads += 1

            apply_update(dom, inc_downloads)
            messages.success(request,
                             render_to_string(
                                 "appstore/partials/view_wiki.html",
                                 {"pre": _("Project copied successfully!")}),
                             extra_tags="html")
            return HttpResponseRedirect(
                reverse('view_app',
                        args=[
                            new_domain.name,
                            new_domain.full_applications()[0].get_id
                        ]))
        else:
            messages.error(request,
                           _("You must specify a name for the new project"))
            return HttpResponseRedirect(
                reverse(ProjectInformationView.urlname, args=[snapshot]))
    else:
        return HttpResponseRedirect(
            reverse(ProjectInformationView.urlname, args=[snapshot]))
Ejemplo n.º 24
0
    def save_copy(self,
                  new_domain_name=None,
                  new_hr_name=None,
                  user=None,
                  copy_by_id=None,
                  share_reminders=True,
                  share_user_roles=True):
        from corehq.apps.app_manager.dbaccessors import get_app
        from corehq.apps.reminders.models import CaseReminderHandler
        from corehq.apps.fixtures.models import FixtureDataItem
        from corehq.apps.app_manager.dbaccessors import get_brief_apps_in_domain
        from corehq.apps.domain.dbaccessors import get_doc_ids_in_domain_by_class
        from corehq.apps.fixtures.models import FixtureDataType
        from corehq.apps.users.models import UserRole

        db = Domain.get_db()
        new_id = db.copy_doc(self.get_id)['id']
        if new_domain_name is None:
            new_domain_name = new_id

        with CriticalSection(
            ['request_domain_name_{}'.format(new_domain_name)]):
            new_domain_name = Domain.generate_name(new_domain_name)
            new_domain = Domain.get(new_id)
            new_domain.name = new_domain_name
            new_domain.hr_name = new_hr_name
            new_domain.copy_history = self.get_updated_history()
            new_domain.is_snapshot = False
            new_domain.snapshot_time = None
            new_domain.organization = None  # TODO: use current user's organization (?)

            # reset stuff
            new_domain.cda.signed = False
            new_domain.cda.date = None
            new_domain.cda.type = None
            new_domain.cda.user_id = None
            new_domain.cda.user_ip = None
            new_domain.is_test = "none"
            new_domain.internal = InternalProperties()
            new_domain.creating_user = user.username if user else None

            for field in self._dirty_fields:
                if hasattr(new_domain, field):
                    delattr(new_domain, field)

            # Saving the domain should happen before we import any apps since
            # importing apps can update the domain object (for example, if user
            # as a case needs to be enabled)
            new_domain.save()

            new_app_components = {}  # a mapping of component's id to its copy

            def copy_data_items(old_type_id, new_type_id):
                for item in FixtureDataItem.by_data_type(
                        self.name, old_type_id):
                    comp = self.copy_component(item.doc_type,
                                               item._id,
                                               new_domain_name,
                                               user=user)
                    comp.data_type_id = new_type_id
                    comp.save()

            def get_latest_app_id(doc_id):
                app = get_app(self.name, doc_id).get_latest_saved()
                if app:
                    return app._id, app.doc_type

            for app in get_brief_apps_in_domain(self.name):
                doc_id, doc_type = app.get_id, app.doc_type
                original_doc_id = doc_id
                if copy_by_id and doc_id not in copy_by_id:
                    continue
                if not self.is_snapshot:
                    doc_id, doc_type = get_latest_app_id(doc_id) or (doc_id,
                                                                     doc_type)
                component = self.copy_component(doc_type,
                                                doc_id,
                                                new_domain_name,
                                                user=user)
                if component:
                    new_app_components[original_doc_id] = component

            for doc_id in get_doc_ids_in_domain_by_class(
                    self.name, FixtureDataType):
                if copy_by_id and doc_id not in copy_by_id:
                    continue
                component = self.copy_component('FixtureDataType',
                                                doc_id,
                                                new_domain_name,
                                                user=user)
                copy_data_items(doc_id, component._id)

            if share_reminders:
                for doc_id in get_doc_ids_in_domain_by_class(
                        self.name, CaseReminderHandler):
                    self.copy_component('CaseReminderHandler',
                                        doc_id,
                                        new_domain_name,
                                        user=user)
            if share_user_roles:
                for doc_id in get_doc_ids_in_domain_by_class(
                        self.name, UserRole):
                    self.copy_component('UserRole',
                                        doc_id,
                                        new_domain_name,
                                        user=user)

        if user:

            def add_dom_to_user(user):
                user.add_domain_membership(new_domain_name, is_admin=True)

            apply_update(user, add_dom_to_user)

        def update_events(handler):
            """
            Change the form_unique_id to the proper form for each event in a newly copied CaseReminderHandler
            """
            from corehq.apps.app_manager.models import FormBase
            for event in handler.events:
                if not event.form_unique_id:
                    continue
                form = FormBase.get_form(event.form_unique_id)
                form_app = form.get_app()
                m_index, f_index = form_app.get_form_location(form.unique_id)
                form_copy = new_app_components[form_app._id].get_module(
                    m_index).get_form(f_index)
                event.form_unique_id = form_copy.unique_id

        def update_for_copy(handler):
            handler.active = False
            update_events(handler)

        if share_reminders:
            for handler in CaseReminderHandler.get_handlers(new_domain_name):
                apply_update(handler, update_for_copy)

        return new_domain
Ejemplo n.º 25
0
def fire_sms_survey_event(reminder, handler, recipients, verified_numbers,
                          logged_event):
    if reminder.callback_try_count > 0:
        # Handle timeouts
        if handler.submit_partial_forms and (
                reminder.callback_try_count == len(
                    reminder.current_event.callback_timeout_intervals)):
            # Submit partial form completions
            for session_id in reminder.xforms_session_ids:
                submit_unfinished_form(session_id,
                                       handler.include_case_side_effects)
        else:
            # Resend current question
            for session_id in reminder.xforms_session_ids:
                session = get_session_by_session_id(session_id)
                if session.end_time is None:
                    vn = VerifiedNumber.view("sms/verified_number_by_owner_id",
                                             key=session.connection_id,
                                             include_docs=True).first()
                    if vn is not None:
                        metadata = MessageMetadata(
                            workflow=get_workflow(handler),
                            reminder_id=reminder._id,
                            xforms_session_couch_id=session._id,
                        )
                        resp = current_question(session_id)
                        send_sms_to_verified_number(vn, resp.event.text_prompt,
                                                    metadata)
    else:
        reminder.xforms_session_ids = []
        domain_obj = Domain.get_by_name(reminder.domain, strict=True)

        # Get the app, module, and form
        try:
            form_unique_id = reminder.current_event.form_unique_id
            form = Form.get_form(form_unique_id)
            app = form.get_app()
            module = form.get_module()
        except Exception:
            logged_event.error(MessagingEvent.ERROR_CANNOT_FIND_FORM)
            return

        # Start a touchforms session for each recipient
        for recipient in recipients:
            logged_subevent = logged_event.create_subevent(
                handler, reminder, recipient)

            verified_number, unverified_number = get_recipient_phone_number(
                reminder, recipient, verified_numbers)

            no_verified_number = verified_number is None
            cant_use_unverified_number = (
                unverified_number is None
                or not domain_obj.send_to_duplicated_case_numbers
                or form_requires_input(form))
            if no_verified_number and cant_use_unverified_number:
                logged_subevent.error(
                    MessagingEvent.ERROR_NO_TWO_WAY_PHONE_NUMBER)
                continue

            key = "start-sms-survey-for-contact-%s" % recipient.get_id
            with CriticalSection([key], timeout=60):
                # Get the case to submit the form against, if any
                if (isinstance(recipient, CommCareCase)
                        and not handler.force_surveys_to_use_triggered_case):
                    case_id = recipient.get_id
                else:
                    case_id = reminder.case_id

                if form.requires_case() and not case_id:
                    logged_subevent.error(MessagingEvent.ERROR_NO_CASE_GIVEN)
                    continue

                # Close all currently open sessions
                SQLXFormsSession.close_all_open_sms_sessions(
                    reminder.domain, recipient.get_id)

                # Start the new session
                try:
                    session, responses = start_session(
                        reminder.domain,
                        recipient,
                        app,
                        module,
                        form,
                        case_id,
                        case_for_case_submission=handler.
                        force_surveys_to_use_triggered_case)
                except TouchformsError as e:
                    human_readable_message = e.response_data.get(
                        'human_readable_message', None)

                    logged_subevent.error(
                        MessagingEvent.ERROR_TOUCHFORMS_ERROR,
                        additional_error_text=human_readable_message)

                    if touchforms_error_is_config_error(e):
                        # Don't reraise the exception because this means there are configuration
                        # issues with the form that need to be fixed
                        continue
                    else:
                        # Reraise the exception so that the framework retries it again later
                        raise
                except Exception as e:
                    logged_subevent.error(
                        MessagingEvent.ERROR_TOUCHFORMS_ERROR)
                    # Reraise the exception so that the framework retries it again later
                    raise
                session.survey_incentive = handler.survey_incentive
                session.workflow = get_workflow(handler)
                session.reminder_id = reminder._id
                session.save()

            reminder.xforms_session_ids.append(session.session_id)
            logged_subevent.xforms_session = session
            logged_subevent.save()

            # Send out first message
            if len(responses) > 0:
                message = format_message_list(responses)
                metadata = MessageMetadata(
                    workflow=get_workflow(handler),
                    reminder_id=reminder._id,
                    xforms_session_couch_id=session._id,
                )
                if verified_number:
                    send_sms_to_verified_number(verified_number, message,
                                                metadata)
                else:
                    send_sms(reminder.domain, recipient, unverified_number,
                             message, metadata)

            logged_subevent.completed()
Ejemplo n.º 26
0
    def handle(self):
        text = ' '.join(self.msg.text.split()[1:])
        is_district = False
        sp = ""
        msd_code = ""
        params = {}

        if text.find(self.DISTRICT_REG_DELIMITER) != -1:
            phrases = [x.strip() for x in text.split(":")]
            if len(phrases) != 2:
                self.respond(REGISTER_HELP)
                return True
            name = phrases[0]
            sp = phrases[1]
            role = Roles.DISTRICT_PHARMACIST
            message = REGISTRATION_CONFIRM_DISTRICT
            is_district = True
        else:
            names = []
            msd_codes = []
            location_regex = '^({prefs})\d+'.format(prefs='|'.join(p.lower() for p in DISTRICT_PREFIXES))
            for the_string in self.args:
                if re.match(location_regex, the_string.strip().lower()):
                    msd_codes.append(the_string.strip().lower())
                else:
                    names.append(the_string)

            name = " ".join(names)
            if len(msd_codes) != 1:
                self.respond(REGISTER_HELP)
                return True
            else:
                [msd_code] = msd_codes

            role = Roles.IN_CHARGE
            message = REGISTRATION_CONFIRM

        if is_district:
            try:
                loc = self._get_district_location(self.domain, sp)
                params['sdp_name'] = loc.name
            except SQLLocation.DoesNotExist:
                self.respond(REGISTER_UNKNOWN_DISTRICT, name=sp)
                return True
        else:
            try:
                loc = self._get_facility_location(self.domain, msd_code)
                params['sdp_name'] = loc.name
                params['msd_code'] = loc.site_code
            except SQLLocation.DoesNotExist:
                self.respond(REGISTER_UNKNOWN_CODE, msd_code=msd_code)
                return True

        split_name = name.split(' ', 2)
        first_name = ''
        last_name = ''
        if len(split_name) == 2:
            first_name, last_name = split_name
        elif split_name:
            first_name = split_name[0]

        if not self.user:
            key = 'generating ils username for %s, %s, %s' % (self.domain, first_name, last_name)
            with CriticalSection([key]):
                username = generate_username(self.domain, first_name, last_name)
                password = uuid.uuid4().hex
                self.user = CommCareUser.create(
                    self.domain,
                    username,
                    password,
                    phone_number=strip_plus(self.msg.phone_number)
                )
            self.verified_contact = self.user.get_or_create_phone_entry(self.msg.phone_number)
            self.verified_contact.set_two_way()
            self.verified_contact.set_verified()
            self.verified_contact.save()
            # As per earlier ILSGateway system, set language by default to Swahili
            self.user.language = 'sw'

        if first_name or last_name:
            self.user.first_name = first_name
            self.user.last_name = last_name
        self.user.set_location(loc)
        self.user.is_active = True
        self.user.user_data['role'] = role
        self.user.save()
        params['contact_name'] = name

        if params:
            self.respond(message, **params)
        return True
Ejemplo n.º 27
0
def request_new_domain(request, form, is_new_user=True):
    now = datetime.utcnow()
    current_user = CouchUser.from_django_user(request.user, strict=True)

    dom_req = RegistrationRequest()
    if is_new_user:
        dom_req.request_time = now
        dom_req.request_ip = get_ip(request)
        dom_req.activation_guid = uuid.uuid1().hex

    project_name = form.cleaned_data.get('hr_name') or form.cleaned_data.get(
        'project_name')
    name = name_to_url(project_name, "project")
    with CriticalSection(['request_domain_name_{}'.format(name)]):
        name = Domain.generate_name(name)
        new_domain = Domain(name=name,
                            hr_name=project_name,
                            is_active=False,
                            date_created=datetime.utcnow(),
                            creating_user=current_user.username,
                            secure_submissions=True,
                            use_sql_backend=True,
                            first_domain_for_user=is_new_user)

        # Avoid projects created by dimagi.com staff members as self started
        new_domain.internal.self_started = not current_user.is_dimagi

        if form.cleaned_data.get('domain_timezone'):
            new_domain.default_timezone = form.cleaned_data['domain_timezone']

        if not is_new_user:
            new_domain.is_active = True

        # ensure no duplicate domain documents get created on cloudant
        new_domain.save(**get_safe_write_kwargs())

    if not new_domain.name:
        new_domain.name = new_domain._id
        new_domain.save()  # we need to get the name from the _id
    dom_req.domain = new_domain.name

    if not settings.ENTERPRISE_MODE:
        _setup_subscription(new_domain.name, current_user)

    UserRole.init_domain_with_presets(new_domain.name)

    if request.user.is_authenticated:
        if not current_user:
            current_user = WebUser()
            current_user.sync_from_django_user(request.user)
            current_user.save()
        current_user.add_domain_membership(new_domain.name, is_admin=True)
        current_user.save()
        dom_req.requesting_user_username = request.user.username
        dom_req.new_user_username = request.user.username
    elif is_new_user:
        _soft_assert_registration_issues(
            f"A new user {request.user.username} was not added to their domain "
            f"{new_domain.name} during registration")

    if is_new_user:
        dom_req.save()
        if settings.IS_SAAS_ENVIRONMENT:
            #  Load template apps to the user's new domain in parallel
            from corehq.apps.app_manager.tasks import load_appcues_template_app
            header = [
                load_appcues_template_app.si(new_domain.name,
                                             current_user.username, slug)
                for slug in APPCUES_APP_SLUGS
            ]
            callback = send_domain_registration_email.si(
                request.user.email, dom_req.domain, dom_req.activation_guid,
                request.user.get_full_name(), request.user.first_name)
            chord(header)(callback)
        else:
            send_domain_registration_email(request.user.email, dom_req.domain,
                                           dom_req.activation_guid,
                                           request.user.get_full_name(),
                                           request.user.first_name)
    send_new_request_update_email(request.user,
                                  get_ip(request),
                                  new_domain.name,
                                  is_new_user=is_new_user)

    send_hubspot_form(HUBSPOT_CREATED_NEW_PROJECT_SPACE_FORM_ID, request)
    return new_domain.name
Ejemplo n.º 28
0
def get_broadcast_edit_critical_section(broadcast_type, broadcast_id):
    return CriticalSection(
        ['edit-broadcast-%s-%s' % (broadcast_type, broadcast_id)],
        timeout=5 * 60)
Ejemplo n.º 29
0
def confirm_domain(request, guid=''):
    with CriticalSection(['confirm_domain_' + guid]):
        error = None
        # Did we get a guid?
        if not guid:
            error = _('An account activation key was not provided.  If you think this '
                      'is an error, please contact the system administrator.')

        # Does guid exist in the system?
        else:
            req = RegistrationRequest.get_by_guid(guid)
            if not req:
                error = _('The account activation key "%s" provided is invalid. If you '
                          'think this is an error, please contact the system '
                          'administrator.') % guid

        if error is not None:
            context = {
                'message_body': error,
                'current_page': {'page_name': 'Account Not Activated'},
            }
            return render(request, 'registration/confirmation_error.html', context)

        requested_domain = Domain.get_by_name(req.domain)
        view_name = "dashboard_default"
        view_args = [requested_domain.name]
        if not domain_has_apps(req.domain):
            if False and settings.IS_SAAS_ENVIRONMENT and domain_is_on_trial(req.domain):
                view_name = "app_from_template"
                view_args.append("appcues")
            else:
                view_name = "default_new_app"

        # Has guid already been confirmed?
        if requested_domain.is_active:
            assert(req.confirm_time is not None and req.confirm_ip is not None)
            messages.success(request, 'Your account %s has already been activated. '
                'No further validation is required.' % req.new_user_username)
            return HttpResponseRedirect(reverse(view_name, args=view_args))

        # Set confirm time and IP; activate domain and new user who is in the
        req.confirm_time = datetime.utcnow()
        req.confirm_ip = get_ip(request)
        req.save()
        requested_domain.is_active = True
        requested_domain.save()
        requesting_user = WebUser.get_by_username(req.new_user_username)

        send_new_request_update_email(requesting_user, get_ip(request), requested_domain.name, is_confirming=True)

        messages.success(request,
                'Your account has been successfully activated.  Thank you for taking '
                'the time to confirm your email address: %s.'
            % (requesting_user.username))
        track_workflow(requesting_user.email, "Confirmed new project")
        track_confirmed_account_on_hubspot.delay(requesting_user)
        request.session['CONFIRM'] = True

        if settings.IS_SAAS_ENVIRONMENT:
            # For AppCues v3, land new user in Web Apps
            view_name = get_cloudcare_urlname(requested_domain.name)
        return HttpResponseRedirect(reverse(view_name, args=view_args))
Ejemplo n.º 30
0
    def save_copy(self, new_domain_name=None, new_hr_name=None, user=None,
                  copy_by_id=None, share_reminders=True,
                  share_user_roles=True):
        from corehq.apps.app_manager.dbaccessors import get_app
        from corehq.apps.data_interfaces.models import AutomaticUpdateRule
        from corehq.apps.reminders.models import CaseReminderHandler
        from corehq.apps.fixtures.models import FixtureDataItem
        from corehq.apps.app_manager.dbaccessors import get_brief_apps_in_domain
        from corehq.apps.domain.dbaccessors import get_doc_ids_in_domain_by_class
        from corehq.apps.fixtures.models import FixtureDataType
        from corehq.apps.users.models import UserRole

        db = Domain.get_db()
        new_id = db.copy_doc(self.get_id)['id']
        if new_domain_name is None:
            new_domain_name = new_id

        uses_new_reminders = project_is_on_new_reminders(self)

        with CriticalSection(['request_domain_name_{}'.format(new_domain_name)]):
            new_domain_name = Domain.generate_name(new_domain_name)
            new_domain = Domain.get(new_id)
            new_domain.name = new_domain_name
            new_domain.hr_name = new_hr_name
            new_domain.copy_history = self.get_updated_history()
            new_domain.is_snapshot = False
            new_domain.snapshot_time = None
            new_domain.organization = None  # TODO: use current user's organization (?)

            # reset stuff
            new_domain.cda.signed = False
            new_domain.cda.date = None
            new_domain.cda.type = None
            new_domain.cda.user_id = None
            new_domain.cda.user_ip = None
            new_domain.is_test = "none"
            new_domain.internal = InternalProperties()
            new_domain.creating_user = user.username if user else None
            new_domain.date_created = datetime.utcnow()
            new_domain.use_sql_backend = True
            new_domain.granted_messaging_access = False

            for field in self._dirty_fields:
                if hasattr(new_domain, field):
                    delattr(new_domain, field)

            # Saving the domain should happen before we import any apps since
            # importing apps can update the domain object (for example, if user
            # as a case needs to be enabled)
            try:
                new_domain.save()
            except PreconditionFailed:
                # This is a hack to resolve http://manage.dimagi.com/default.asp?241492
                # Following solution in
                # https://github.com/dimagi/commcare-hq/commit/d59b1e403060ade599cc4a03db0aabc4da62b668
                time.sleep(0.5)
                new_domain.save()

            new_app_components = {}  # a mapping of component's id to its copy

            def copy_data_items(old_type_id, new_type_id):
                for item in FixtureDataItem.by_data_type(self.name, old_type_id):
                    comp = self.copy_component(
                        item.doc_type, item._id, new_domain_name, user=user)
                    comp.data_type_id = new_type_id
                    comp.save()

            def get_latest_app_id(doc_id):
                app = get_app(self.name, doc_id).get_latest_saved()
                if app:
                    return app._id, app.doc_type

            for app in get_brief_apps_in_domain(self.name):
                doc_id, doc_type = app.get_id, app.doc_type
                original_doc_id = doc_id
                if copy_by_id and doc_id not in copy_by_id:
                    continue
                if not self.is_snapshot:
                    doc_id, doc_type = get_latest_app_id(doc_id) or (doc_id, doc_type)
                component = self.copy_component(doc_type, doc_id, new_domain_name, user=user)
                if component:
                    new_app_components[original_doc_id] = component

            for doc_id in get_doc_ids_in_domain_by_class(self.name, FixtureDataType):
                if copy_by_id and doc_id not in copy_by_id:
                    continue
                component = self.copy_component(
                    'FixtureDataType', doc_id, new_domain_name, user=user)
                copy_data_items(doc_id, component._id)

            def convert_form_unique_id_function(form_unique_id):
                from corehq.apps.app_manager.models import FormBase
                form = FormBase.get_form(form_unique_id)
                form_app = form.get_app()
                m_index, f_index = form_app.get_form_location(form.unique_id)
                form_copy = new_app_components[form_app._id].get_module(m_index).get_form(f_index)
                return form_copy.unique_id

            if share_reminders:
                if uses_new_reminders:
                    for rule in AutomaticUpdateRule.by_domain(
                        self.name,
                        AutomaticUpdateRule.WORKFLOW_SCHEDULING,
                        active_only=False,
                    ):
                        rule.copy_conditional_alert(
                            new_domain_name,
                            convert_form_unique_id_function=convert_form_unique_id_function,
                        )
                else:
                    for doc_id in get_doc_ids_in_domain_by_class(self.name, CaseReminderHandler):
                        self.copy_component(
                            'CaseReminderHandler', doc_id, new_domain_name, user=user)
            if share_user_roles:
                for doc_id in get_doc_ids_in_domain_by_class(self.name, UserRole):
                    self.copy_component('UserRole', doc_id, new_domain_name, user=user)

        if user:
            def add_dom_to_user(user):
                user.add_domain_membership(new_domain_name, is_admin=True)
            apply_update(user, add_dom_to_user)

        if not uses_new_reminders:
            # When uses_new_reminders is True, all of this is already taken care of
            # in the copy process
            def update_events(handler):
                """
                Change the form_unique_id to the proper form for each event in a newly copied CaseReminderHandler
                """
                for event in handler.events:
                    if not event.form_unique_id:
                        continue
                    event.form_unique_id = convert_form_unique_id_function(event.form_unique_id)

            def update_for_copy(handler):
                handler.active = False
                update_events(handler)

            if share_reminders:
                for handler in CaseReminderHandler.get_handlers(new_domain_name):
                    apply_update(handler, update_for_copy)

        return new_domain