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
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)
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)
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]))
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} )
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
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), ])
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)])
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)
def critical_section_for_smsforms_sessions(contact_id): return CriticalSection( ['smsforms-sessions-lock-for-contact-%s' % contact_id], timeout=5 * 60)
def sync_usercase(user): with CriticalSection(get_sync_lock_key(user._id)): _UserCaseHelper.commit(list(_iter_sync_usercase_helpers(user)))
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
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
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)
def get_conditional_alert_edit_critical_section(rule_id): return CriticalSection(['edit-conditional-alert-%s' % rule_id], timeout=5 * 60)
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
def update_group_membership(request, domain, group_id): with CriticalSection(['update-group-membership-%s' % group_id]): return _update_group_membership(request, domain, group_id)
def get_or_create_phone_entry(self, phone_number): with CriticalSection([self.phone_sync_key]): return self._get_or_create_phone_entry(phone_number)
def _critical_section(channel): return CriticalSection([ f'XFormsSessionSynchronization.critical_section.{channel.backend_id}/{channel.phone_number}' ], timeout=5 * 60)
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
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)
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)
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]))
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
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()
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
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
def get_broadcast_edit_critical_section(broadcast_type, broadcast_id): return CriticalSection( ['edit-broadcast-%s-%s' % (broadcast_type, broadcast_id)], timeout=5 * 60)
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))
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