def validate_spec(self, spec): value = spec.get(self.column_id) if isinstance(value, str): try: string_to_boolean(value) if value else None except ValueError: return self.error_message
def validate_spec(self, spec): is_active = spec.get('is_active') if isinstance(is_active, str): try: string_to_boolean(is_active) if is_active else None except ValueError: return self.error_message
def get_cases(request, domain): request_params = request.GET if request.couch_user.is_commcare_user(): user_id = request.couch_user.get_id else: user_id = request_params.get("user_id", "") if not user_id and not request.couch_user.is_web_user(): return HttpResponseBadRequest("Must specify user_id!") ids_only = string_to_boolean(request_params.get("ids_only", "false")) case_id = request_params.get("case_id", "") footprint = string_to_boolean(request_params.get("footprint", "false")) accessor = CaseAccessors(domain) if toggles.HSPH_HACK.enabled(domain): hsph_case_id = request_params.get('hsph_hack', None) if hsph_case_id != 'None' and hsph_case_id and user_id: case = accessor.get_case(hsph_case_id) usercase_id = CommCareUser.get_by_user_id( user_id).get_usercase_id() usercase = accessor.get_case(usercase_id) if usercase_id else None return json_response( map( lambda case: CaseAPIResult( id=case['_id'], couch_doc=case, id_only=ids_only), filter(None, [case, case.parent, usercase])))
def get_cases(request, domain): if request.couch_user.is_commcare_user(): user_id = request.couch_user.get_id else: user_id = request.REQUEST.get("user_id", "") if not user_id and not request.couch_user.is_web_user(): return HttpResponseBadRequest("Must specify user_id!") ids_only = string_to_boolean(request.REQUEST.get("ids_only", "false")) case_id = request.REQUEST.get("case_id", "") footprint = string_to_boolean(request.REQUEST.get("footprint", "false")) include_children = string_to_boolean( request.REQUEST.get("include_children", "false")) if case_id and not footprint and not include_children: # short circuit everything else and just return the case # NOTE: this allows any user in the domain to access any case given # they know its ID, which is slightly different from the previous # behavior (can only access things you own + footprint). If we want to # change this contract we would need to update this to check the # owned case list + footprint case = CommCareCase.get(case_id) assert case.domain == domain cases = [CaseAPIResult(id=case_id, couch_doc=case, id_only=ids_only)]
def get_cases(request, domain): if request.couch_user.is_commcare_user(): user_id = request.couch_user.get_id else: user_id = request.REQUEST.get("user_id", "") if not user_id and not request.couch_user.is_web_user(): return HttpResponseBadRequest("Must specify user_id!") ids_only = string_to_boolean(request.REQUEST.get("ids_only", "false")) case_id = request.REQUEST.get("case_id", "") footprint = string_to_boolean(request.REQUEST.get("footprint", "false")) include_children = string_to_boolean(request.REQUEST.get("include_children", "false")) if case_id and not footprint and not include_children: # short circuit everything else and just return the case # NOTE: this allows any user in the domain to access any case given # they know its ID, which is slightly different from the previous # behavior (can only access things you own + footprint). If we want to # change this contract we would need to update this to check the # owned case list + footprint case = CommCareCase.get(case_id) assert case.domain == domain cases = [CaseAPIResult(id=case_id, couch_doc=case, id_only=ids_only)] else: filters = get_filters_from_request(request) status = api_closed_to_status(request.REQUEST.get('closed', 'false')) case_type = filters.get('properties/case_type', None) cases = get_filtered_cases(domain, status=status, case_type=case_type, user_id=user_id, filters=filters, footprint=footprint, ids_only=ids_only, strip_history=True, include_children=include_children) return json_response(cases)
def export_data(req, domain): """ Download all data for a couchdbkit model """ try: export_tag = json.loads(req.GET.get("export_tag", "null") or "null") except ValueError: return HttpResponseBadRequest() group, users = util.get_group_params(domain, **json_request(req.GET)) include_errors = string_to_boolean(req.GET.get("include_errors", False)) kwargs = { "format": req.GET.get("format", Format.XLS_2007), "previous_export_id": req.GET.get("previous_export", None), "filename": export_tag, "use_cache": string_to_boolean(req.GET.get("use_cache", "True")), "max_column_size": int(req.GET.get("max_column_size", 2000)), "separator": req.GET.get("separator", "|") } user_filter, _ = UserTypeFilter.get_user_filter(req) if user_filter: filtered_users = users_matching_filter(domain, user_filter) def _ufilter(user): try: return user['form']['meta']['userID'] in filtered_users except KeyError: return False filter = _ufilter else: filter = SerializableFunction(util.group_filter, group=group) errors_filter = instances if not include_errors else None kwargs['filter'] = couchexport.util.intersect_functions( filter, errors_filter) if kwargs['format'] == 'raw': resp = export_raw_data([domain, export_tag], filename=export_tag) else: try: resp = export_data_shared([domain, export_tag], **kwargs) except CouchExportException as e: return HttpResponseBadRequest(e) if resp: return resp else: messages.error( req, "Sorry, there was no data found for the tag '%s'." % export_tag) next = req.GET.get("next", "") if not next: next = export.ExcelExportReport.get_url(domain=domain) return HttpResponseRedirect(next)
def handle(self, *args, **options): direction = BOTH if len(args) < 1 else args[0].strip().lower() if not direction in SYNC_DIRECTIONS: raise CommandError( '"%s" is not a valid sync direction. It must be one of %s' % (direction, ", ".join(('"%s"' % dir for dir in SYNC_DIRECTIONS))) ) continuous = False if len(args) < 2 else string_to_boolean(args[1].strip()) cancel = False if len(args) < 3 else string_to_boolean(args[1].strip()) do_sync(direction, continuous, cancel)
def export_data(req, domain): """ Download all data for a couchdbkit model """ try: export_tag = json.loads(req.GET.get("export_tag", "null") or "null") except ValueError: return HttpResponseBadRequest() group, users = util.get_group_params(domain, **json_request(req.GET)) include_errors = string_to_boolean(req.GET.get("include_errors", False)) kwargs = { "format": req.GET.get("format", Format.XLS_2007), "previous_export_id": req.GET.get("previous_export", None), "filename": export_tag, "use_cache": string_to_boolean(req.GET.get("use_cache", "True")), "max_column_size": int(req.GET.get("max_column_size", 2000)), "separator": req.GET.get("separator", "|"), } user_filter, _ = UserTypeFilter.get_user_filter(req) if user_filter: filtered_users = users_matching_filter(domain, user_filter) def _ufilter(user): try: return user["form"]["meta"]["userID"] in filtered_users except KeyError: return False filter = _ufilter else: filter = SerializableFunction(util.group_filter, group=group) errors_filter = instances if not include_errors else None kwargs["filter"] = couchexport.util.intersect_functions(filter, errors_filter) if kwargs["format"] == "raw": resp = export_raw_data([domain, export_tag], filename=export_tag) else: try: resp = export_data_shared([domain, export_tag], **kwargs) except CouchExportException as e: return HttpResponseBadRequest(e) if resp: return resp else: messages.error(req, "Sorry, there was no data found for the tag '%s'." % export_tag) next = req.GET.get("next", "") if not next: next = export.ExcelExportReport.get_url(domain=domain) return HttpResponseRedirect(next)
def get_cases_skip_arg(request, domain): """ When this function returns True, skippable_quickcache will not go to the cache for the result. By default, if neither of these params are passed into the function, nothing will be cached. Cache will always be skipped if ids_only is false. The caching is mainly a hack for touchforms to respond more quickly. Touchforms makes repeated requests to get the list of case_ids associated with a user. """ request_params = request.GET return (not string_to_boolean(request_params.get('use_cache', 'false')) or not string_to_boolean(request_params.get('ids_only', 'false')))
def export_data(req, domain): """ Download all data for a couchdbkit model """ try: export_tag = json.loads(req.GET.get("export_tag", "null") or "null") except ValueError: return HttpResponseBadRequest() group, users = util.get_group_params(domain, **json_request(req.GET)) include_errors = string_to_boolean(req.GET.get("include_errors", False)) kwargs = {"format": req.GET.get("format", Format.XLS_2007), "previous_export_id": req.GET.get("previous_export", None), "filename": export_tag, "use_cache": string_to_boolean(req.GET.get("use_cache", "True")), "max_column_size": int(req.GET.get("max_column_size", 2000)), "separator": req.GET.get("separator", "|")} user_filter, _ = FilterUsersField.get_user_filter(req) if user_filter: users_matching_filter = map(lambda x: x.get('user_id'), get_all_users_by_domain(domain, user_filter=user_filter, simplified=True)) def _ufilter(user): try: return user['form']['meta']['userID'] in users_matching_filter except KeyError: return False filter = _ufilter else: filter = SerializableFunction(util.group_filter, group=group) errors_filter = instances if not include_errors else None kwargs['filter'] = couchexport.util.intersect_functions(filter, errors_filter) if kwargs['format'] == 'raw': resp = export_raw_data([domain, export_tag], filename=export_tag) else: try: resp = export_data_shared([domain,export_tag], **kwargs) except UnsupportedExportFormat as e: return HttpResponseBadRequest(e) if resp: return resp else: messages.error(req, "Sorry, there was no data found for the tag '%s'." % export_tag) next = req.GET.get("next", "") if not next: next = export.ExcelExportReport.get_url(domain) return HttpResponseRedirect(next)
def get_cases_skip_arg(request, domain): """ When this function returns True, skippable_quickcache will not go to the cache for the result. By default, if neither of these params are passed into the function, nothing will be cached. Cache will always be skipped if ids_only is false. The caching is mainly a hack for touchforms to respond more quickly. Touchforms makes repeated requests to get the list of case_ids associated with a user. """ if not toggles.CLOUDCARE_CACHE.enabled(domain): return True return (not string_to_boolean(request.REQUEST.get('use_cache', 'false')) or not string_to_boolean(request.REQUEST.get('ids_only', 'false')))
def __init__(self, tests, runner): reuse_db = (CmdLineParametersPlugin.get('reusedb') or string_to_boolean(os.environ.get("REUSE_DB") or "0")) self.reuse_db = reuse_db self.skip_setup_for_reuse_db = reuse_db and reuse_db != "reset" self.skip_teardown_for_reuse_db = reuse_db and reuse_db != "teardown" super(HqdbContext, self).__init__(tests, runner)
def filter_set(self): """ Whether a report has any filters set. Based on whether or not there is a query string. This gets carried to additional asynchronous calls """ return string_to_boolean(self.request.GET.get("filterSet")) \ if "filterSet" in self.request.GET else bool(self.request.META.get('QUERY_STRING'))
def recipients_filter(self, user): roles = user.user_data.get('role', []) if not roles: return False needs_reminders = string_to_boolean(user.user_data.get('needs_reminders', "False")) return any([role != IN_CHARGE_ROLE for role in user.user_data.get('role', [])]) and \ needs_reminders and user.location_id
def api_send_sms(request, domain): """ An API to send SMS. Expected post parameters: phone_number - the phone number to send to contact_id - the _id of a contact to send to (overrides phone_number) text - the text of the message backend_id - the name of the MobileBackend to use while sending """ if request.method == "POST": phone_number = request.POST.get("phone_number", None) contact_id = request.POST.get("contact_id", None) text = request.POST.get("text", None) backend_id = request.POST.get("backend_id", None) chat = request.POST.get("chat", None) if (phone_number is None and contact_id is None) or (text is None): return HttpResponseBadRequest("Not enough arguments.") vn = None if contact_id is not None: try: contact = get_contact(contact_id) assert contact is not None assert contact.domain == domain except Exception: return HttpResponseBadRequest("Contact not found.") try: vn = contact.get_verified_number() assert vn is not None phone_number = vn.phone_number except Exception: return HttpResponseBadRequest("Contact has no phone number.") try: chat_workflow = string_to_boolean(chat) except Exception: chat_workflow = False if chat_workflow: chat_user_id = request.couch_user._id else: chat_user_id = None metadata = MessageMetadata( chat_user_id=chat_user_id ) if backend_id is not None: success = send_sms_with_backend_name(domain, phone_number, text, backend_id, metadata) elif vn is not None: success = send_sms_to_verified_number(vn, text, metadata) else: success = send_sms(domain, None, phone_number, text, metadata) if success: return HttpResponse("OK") else: return HttpResponse("ERROR") else: return HttpResponseBadRequest("POST Expected.")
def app_list(request, domain, urlPath): preview = string_to_boolean(request.REQUEST.get("preview", "false")) language = request.couch_user.language or "en" def _app_latest_build_json(app_id): build = ApplicationBase.view('app_manager/saved_app', startkey=[domain, app["_id"], {}], endkey=[domain, app["_id"]], descending=True, limit=1).one() return build._doc if build else None if not preview: apps = get_cloudcare_apps(domain) # replace the apps with the last build of each app apps = [_app_latest_build_json(app["_id"])for app in apps] else: apps = ApplicationBase.view('app_manager/applications_brief', startkey=[domain], endkey=[domain, {}]) apps = [app._doc for app in apps if app and app.application_version == "2.0"] # trim out empty apps apps = filter(lambda app: app, apps) return render_to_response(request, "cloudcare/cloudcare_home.html", {"domain": domain, "language": language, "apps": json.dumps(apps), "apps_raw": apps, "preview": preview, "maps_api_key": settings.GMAPS_API_KEY })
def get_cases(request, domain): request_params = request.GET if request.couch_user.is_commcare_user(): user_id = request.couch_user.get_id else: user_id = request_params.get("user_id", "") if not user_id and not request.couch_user.is_web_user(): return HttpResponseBadRequest("Must specify user_id!") ids_only = string_to_boolean(request_params.get("ids_only", "false")) case_id = request_params.get("case_id", "") footprint = string_to_boolean(request_params.get("footprint", "false")) accessor = CaseAccessors(domain) if toggles.HSPH_HACK.enabled(domain): hsph_case_id = request_params.get('hsph_hack', None) if hsph_case_id != 'None' and hsph_case_id and user_id: case = accessor.get_case(hsph_case_id) usercase_id = CommCareUser.get_by_user_id(user_id).get_usercase_id() usercase = accessor.get_case(usercase_id) if usercase_id else None return json_response(map( lambda case: CaseAPIResult(domain=domain, id=case['_id'], couch_doc=case, id_only=ids_only), filter(None, [case, case.parent, usercase]) )) if case_id and not footprint: # short circuit everything else and just return the case # NOTE: this allows any user in the domain to access any case given # they know its ID, which is slightly different from the previous # behavior (can only access things you own + footprint). If we want to # change this contract we would need to update this to check the # owned case list + footprint case = accessor.get_case(case_id) assert case.domain == domain cases = [CaseAPIResult(domain=domain, id=case_id, couch_doc=case, id_only=ids_only)] else: filters = get_filters_from_request_params(request_params) status = api_closed_to_status(request_params.get('closed', 'false')) case_type = filters.get('properties/case_type', None) cases = get_filtered_cases(domain, status=status, case_type=case_type, user_id=user_id, filters=filters, footprint=footprint, ids_only=ids_only, strip_history=True) return json_response(cases)
def spec_value_to_boolean_or_none(user_spec_dict, key): value = user_spec_dict.get(key, None) if value and isinstance(value, str): return string_to_boolean(value) elif isinstance(value, bool): return value else: return None
def recipients_filter(self, user): roles = user.user_data.get('role', []) if not roles: return False needs_reminders = string_to_boolean( user.user_data.get('needs_reminders', "False")) return any([role != IN_CHARGE_ROLE for role in user.user_data.get('role', [])]) and \ needs_reminders and user.location_id
def are_you_sure(prompt="Are you sure you want to proceed? (yes or no): "): """ Ask a user if they are sure before doing something. Return whether or not they are sure """ should_proceed = raw_input(prompt) try: return string_to_boolean(should_proceed) except Exception: return False
def are_you_sure(prompt="Are you sure you want to proceed? (yes or no): "): """ Ask a user if they are sure before doing something. Return whether or not they are sure """ should_proceed = input(prompt) try: return string_to_boolean(should_proceed) except Exception: return False
def filter_set(self): """ Whether a report has any filters set. Based on whether or not there is a query string. This gets carried to additional asynchronous calls """ are_filters_set = bool(self.request.META.get('QUERY_STRING')) if "filterSet" in self.request.GET: try: are_filters_set = string_to_boolean(self.request.GET.get("filterSet")) except ValueError: # not a parseable boolean pass return are_filters_set
def patient_from_instance(doc): """ From a registration document object, create a Patient. """ # TODO: clean up / un hard-code patient = CPatient(first_name=doc["first_name"], last_name=doc["last_name"], birthdate=string_to_datetime(doc["birthdate"]).date() if doc["birthdate"] else None, birthdate_estimated=string_to_boolean(doc["birthdate_estimated"]), gender=doc["gender"], patient_id=doc["patient_id"], created_on=datetime.utcnow()) return patient
def load_locations(path): info("Loading locations %s" % (path)) if not os.path.exists(path): raise Exception("no file found at %s" % path) count = 0 with open(path, 'r') as f: reader = csv.reader(f, delimiter=',', quotechar='"') for row in reader: id, name, is_active, msd_code, parent_name, parent_type, lat, lon, group, type = row # for now assumes these are already create loc_type = LocationType.objects.get(name__iexact=type) parent = Location.objects.get(name__iexact=parent_name, type__name__iexact=parent_type) \ if parent_name and parent_type else None if lat and lon: if Point.objects.filter(longitude=lon, latitude=lat).exists(): point = Point.objects.filter(longitude=lon, latitude=lat)[0] else: point = Point.objects.create(longitude=lon, latitude=lat) else: point = None code = msd_code if msd_code else _get_code(type, name) try: l = Location.objects.get(code=code) except Location.DoesNotExist: l = Location(code=code) l.name = name l.type = loc_type l.is_active = string_to_boolean(is_active) if parent: l.parent = parent if point: l.point = point l.save() sp = supply_point_from_location\ (l, SupplyPointType.objects.get(name__iexact=type), SupplyPoint.objects.get(location=parent) if parent else None) if group: group_obj = SupplyPointGroup.objects.get_or_create( code=group)[0] sp.groups.add(group_obj) sp.save() count += 1 print "Processed %d locations" % count
def load_locations(path): info("Loading locations %s" % (path)) if not os.path.exists(path): raise Exception("no file found at %s" % path) count = 0 with open(path, 'r') as f: reader = csv.reader(f, delimiter=',', quotechar='"') for row in reader: id, name, is_active, msd_code, parent_name, parent_type, lat, lon, group, type = row # for now assumes these are already create loc_type = LocationType.objects.get(name__iexact=type) parent = Location.objects.get(name__iexact=parent_name, type__name__iexact=parent_type) \ if parent_name and parent_type else None if lat and lon: if Point.objects.filter(longitude=lon, latitude=lat).exists(): point = Point.objects.filter(longitude=lon, latitude=lat)[0] else: point = Point.objects.create(longitude=lon, latitude=lat) else: point = None code = msd_code if msd_code else _get_code(type, name) try: l = Location.objects.get(code=code) except Location.DoesNotExist: l = Location(code=code) l.name = name l.type = loc_type l.is_active = string_to_boolean(is_active) if parent: l.parent = parent if point: l.point = point l.save() sp = supply_point_from_location\ (l, SupplyPointType.objects.get(name__iexact=type), SupplyPoint.objects.get(location=parent) if parent else None) if group: group_obj = SupplyPointGroup.objects.get_or_create(code=group)[0] sp.groups.add(group_obj) sp.save() count += 1 print "Processed %d locations" % count
def test(request): dynamic = string_to_boolean(request.GET["dynamic"]) if "dynamic" in request.GET else True template = request.GET["template"] if "template" in request.GET \ else "touchscreen/example-inner.html" header = request.GET["header"] if "header" in request.GET \ else "Hello World!" pat_id = request.GET["id"] if "id" in request.GET \ else "000000000001" try: patient = loader.get_patient(pat_id) except Exception: patient = None if dynamic: return render_to_response(request, "touchscreen/wrapper-dynamic.html", {"header": header, "template": template, "patient": patient, "options": TouchscreenOptions.default()}) else: return render_to_response(request, template, {"patient": patient, "options": TouchscreenOptions.default()})
def obj_get_list(self, bundle, **kwargs): domain = kwargs['domain'] project = getattr(bundle.request, 'project', self.domain_obj(domain)) parent_id = bundle.request.GET.get('parent_id', None) try: include_inactive = string_to_boolean( bundle.request.GET.get('include_inactive', False)) except ValueError: include_inactive = False user = bundle.request.couch_user if not parent_id: if not user.has_permission(domain, 'access_all_locations'): raise BadRequest(LOCATION_ACCESS_DENIED) return SQLLocation.root_locations(domain, include_inactive) else: if not user_can_access_location_id(kwargs['domain'], user, parent_id): raise BadRequest(LOCATION_ACCESS_DENIED) parent = get_location_or_not_exist(parent_id, domain) return self.child_queryset(domain, include_inactive, parent)
def delete_user(request): if request.method == "POST": data = json.loads(request.POST.get('result')) if not data: return HttpResponseRedirect(reverse('bhoma_admin')) if string_to_boolean(data.get("confirm")): user = User.objects.get(username=data.get("username")) if request.user == user: return render_to_response(request, "touchscreen/error.html", {"error_text": "You can't delete the currently logged in user account. " "Please logout and log in as a different user", "options": TouchscreenOptions.admin()}) else: user.delete() return HttpResponseRedirect(reverse("bhoma_admin")) return render_to_response(request, "bhoma_touchscreen.html", {'form': {'name': 'delete user', 'wfobj': 'wfDeleteUser'}, 'mode': 'workflow', 'dynamic_scripts': ["%swebapp/javascripts/user_del.js" % \ settings.STATIC_URL,] })
def spec_value_to_boolean_or_none(user_spec_dict, key): value = user_spec_dict.get(key) or None if value and isinstance(value, str): return string_to_boolean(value) return value
def _safe_bool(bundle, param, default=False): try: return string_to_boolean(bundle.request.GET.get(param)) except ValueError: return default
def create_or_update_users_and_groups(domain, user_specs, group_memoizer=None, update_progress=None): ret = {"errors": [], "rows": []} group_memoizer = group_memoizer or GroupMemoizer(domain) group_memoizer.load_all() current = 0 can_assign_locations = domain_has_privilege(domain, privileges.LOCATIONS) if can_assign_locations: location_cache = SiteCodeToLocationCache(domain) domain_obj = Domain.get_by_name(domain) allowed_group_names = [group.name for group in group_memoizer.groups] roles_by_name = {role.name: role for role in UserRole.by_domain(domain)} validators = get_user_import_validators(domain_obj, user_specs, allowed_group_names, list(roles_by_name)) try: for row in user_specs: if update_progress: update_progress(current) current += 1 username = row.get('username') status_row = { 'username': username, 'row': row, } try: for validator in validators: validator(row) except UserUploadError as e: status_row['flag'] = str(e) ret['rows'].append(status_row) continue data = row.get('data') email = row.get('email') group_names = list(map(str, row.get('group') or [])) language = row.get('language') name = row.get('name') password = row.get('password') phone_number = row.get('phone-number') uncategorized_data = row.get('uncategorized_data') user_id = row.get('user_id') location_codes = row.get('location_code') or [] if location_codes and not isinstance(location_codes, list): location_codes = [location_codes] # ignore empty location_codes = [code for code in location_codes if code] role = row.get('role', '') try: username = normalize_username(str(username), domain) if username else None password = str(password) if password else None is_active = row.get('is_active') or None if is_active and isinstance(is_active, str): is_active = string_to_boolean(is_active) if user_id: user = CommCareUser.get_by_user_id(user_id, domain) if not user: raise UserUploadError( _("User with ID '{user_id}' not found").format( user_id=user_id, domain=domain)) if username and user.username != username: raise UserUploadError( _('Changing usernames is not supported: %(username)r to %(new_username)r' ) % { 'username': user.username, 'new_username': username }) if is_password(password): user.set_password(password) status_row['flag'] = 'updated' else: user = CommCareUser.create(domain, username, password, commit=False) status_row['flag'] = 'created' if phone_number: user.add_phone_number(_fmt_phone(phone_number), default=True) if name: user.set_full_name(str(name)) if data: user.user_data.update(data) if uncategorized_data: user.user_data.update(uncategorized_data) if language: user.language = language if email: user.email = email.lower() if is_active is not None: user.is_active = is_active if can_assign_locations: # Do this here so that we validate the location code before we # save any other information to the user, this way either all of # the user's information is updated, or none of it location_ids = [] for code in location_codes: loc = get_location_from_site_code(code, location_cache) location_ids.append(loc.location_id) locations_updated = set( user.assigned_location_ids) != set(location_ids) primary_location_removed = ( user.location_id and not location_ids or user.location_id not in location_ids) if primary_location_removed: user.unset_location(commit=False) if locations_updated: user.reset_locations(location_ids, commit=False) if role: user.set_role(domain, roles_by_name[role].get_qualified_id()) user.save() if is_password(password): # Without this line, digest auth doesn't work. # With this line, digest auth works. # Other than that, I'm not sure what's going on # Passing use_primary_db=True because of https://dimagi-dev.atlassian.net/browse/ICDS-465 user.get_django_user( use_primary_db=True).check_password(password) for group in group_memoizer.by_user_id(user.user_id): if group.name not in group_names: group.remove_user(user) for group_name in group_names: group_memoizer.by_name(group_name).add_user(user, save=False) except (UserUploadError, CouchUser.Inconsistent) as e: status_row['flag'] = str(e) ret["rows"].append(status_row) finally: try: group_memoizer.save_all() except BulkSaveError as e: _error_message = ( "Oops! We were not able to save some of your group changes. " "Please make sure no one else is editing your groups " "and try again.") logging.exception(('BulkSaveError saving groups. ' 'User saw error message "%s". Errors: %s') % (_error_message, e.errors)) ret['errors'].append(_error_message) return ret
def user_needs_reminders(user): return string_to_boolean(user.user_data.get('needs_reminders', 'False'))
def cloudcare_main(request, domain, urlPath): try: preview = string_to_boolean(request.REQUEST.get("preview", "false")) except ValueError: # this is typically only set at all if it's intended to be true so this # is a reasonable default for "something went wrong" preview = True app_access = ApplicationAccess.get_by_domain(domain) def _app_latest_build_json(app_id): build = ApplicationBase.view('app_manager/saved_app', startkey=[domain, app_id, {}], endkey=[domain, app_id], descending=True, limit=1).one() return get_app_json(build) if build else None if not preview: apps = get_cloudcare_apps(domain) # replace the apps with the last build of each app apps = [_app_latest_build_json(app["_id"]) for app in apps] else: apps = ApplicationBase.view('app_manager/applications_brief', startkey=[domain], endkey=[domain, {}]) apps = [get_app_json(app) for app in apps if app and app.application_version == V2] # trim out empty apps apps = filter(lambda app: app, apps) apps = filter(lambda app: app_access.user_can_access_app(request.couch_user, app), apps) def _default_lang(): if apps: # unfortunately we have to go back to the DB to find this return Application.get(apps[0]["_id"]).build_langs[0] else: return "en" # default language to user's preference, followed by # first app's default, followed by english language = request.couch_user.language or _default_lang() def _url_context(): # given a url path, returns potentially the app and case, if they're # selected. the front end optimizes with these to avoid excess server # calls # there's an annoying dependency between this logic and backbone's # url routing that seems hard to solve well. this needs to be synced # with apps.js if anything changes # for apps anything with "view/app/" works # for cases it will be: # "view/:app/:module/:form/case/:case/" # could use regex here but this is actually simpler with the potential # absence of a trailing slash split = urlPath.split('/') app_id = split[1] if len(split) >= 2 else None case_id = split[5] if len(split) >= 6 else None app = None if app_id: if app_id in [a['_id'] for a in apps]: app = look_up_app_json(domain, app_id) else: messages.info(request, _("That app is no longer valid. Try using the " "navigation links to select an app.")) if app is None and len(apps) == 1: app = look_up_app_json(domain, apps[0]['_id']) def _get_case(domain, case_id): case = CommCareCase.get(case_id) assert case.domain == domain, "case %s not in %s" % (case_id, domain) return case.get_json() case = _get_case(domain, case_id) if case_id else None return { "app": app, "case": case } context = { "domain": domain, "language": language, "apps": apps, "apps_raw": apps, "preview": preview, "maps_api_key": settings.GMAPS_API_KEY } context.update(_url_context()) return render(request, "cloudcare/cloudcare_home.html", context)
def cloudcare_main(request, domain, urlPath): preview = string_to_boolean(request.REQUEST.get("preview", "false")) app_access = ApplicationAccess.get_by_domain(domain) def _app_latest_build_json(app_id): build = ApplicationBase.view('app_manager/saved_app', startkey=[domain, app_id, {}], endkey=[domain, app_id], descending=True, limit=1).one() return build._doc if build else None if not preview: apps = get_cloudcare_apps(domain) # replace the apps with the last build of each app apps = [_app_latest_build_json(app["_id"]) for app in apps] else: apps = ApplicationBase.view('app_manager/applications_brief', startkey=[domain], endkey=[domain, {}]) apps = [ app._doc for app in apps if app and app.application_version == "2.0" ] # trim out empty apps apps = filter(lambda app: app, apps) apps = filter( lambda app: app_access.user_can_access_app(request.couch_user, app), apps) def _default_lang(): if apps: # unfortunately we have to go back to the DB to find this return Application.get(apps[0]["_id"]).build_langs[0] else: return "en" # default language to user's preference, followed by # first app's default, followed by english language = request.couch_user.language or _default_lang() def _url_context(): # given a url path, returns potentially the app and case, if they're # selected. the front end optimizes with these to avoid excess server # calls # there's an annoying dependency between this logic and backbone's # url routing that seems hard to solve well. this needs to be synced # with apps.js if anything changes # for apps anything with "view/app/" works # for cases it will be: # "view/:app/:module/:form/case/:case/" # could use regex here but this is actually simpler with the potential # absence of a trailing slash split = urlPath.split('/') app_id = split[1] if len(split) >= 2 else None case_id = split[5] if len(split) >= 6 else None app = None if app_id: if app_id in [a['_id'] for a in apps]: app = get_app(domain, app_id) else: messages.info( request, _("That app is no longer valid. Try using the " "navigation links to select an app.")) if app == None and len(apps) == 1: app = get_app(domain, apps[0]['_id']) def _get_case(domain, case_id): case = CommCareCase.get(case_id) assert case.domain == domain, "case %s not in %s" % (case_id, domain) return case.get_json() case = _get_case(domain, case_id) if case_id else None return {"app": app, "case": case}
def cloudcare_main(request, domain, urlPath): try: preview = string_to_boolean(request.REQUEST.get("preview", "false")) except ValueError: # this is typically only set at all if it's intended to be true so this # is a reasonable default for "something went wrong" preview = True app_access = ApplicationAccess.get_by_domain(domain) if not preview: apps = get_cloudcare_apps(domain) if request.project.use_cloudcare_releases: # replace the apps with the last starred build of each app, removing the ones that aren't starred apps = filter(lambda app: app.is_released, [get_app(domain, app['_id'], latest=True) for app in apps]) # convert to json apps = [get_app_json(app) for app in apps] else: # legacy functionality - use the latest build regardless of stars apps = [get_app_json(ApplicationBase.get_latest_build(domain, app['_id'])) for app in apps] else: apps = ApplicationBase.view('app_manager/applications_brief', startkey=[domain], endkey=[domain, {}]) apps = [get_app_json(app) for app in apps if app and app.application_version == V2] # trim out empty apps apps = filter(lambda app: app, apps) apps = filter(lambda app: app_access.user_can_access_app(request.couch_user, app), apps) def _default_lang(): if apps: # unfortunately we have to go back to the DB to find this return Application.get(apps[0]["_id"]).build_langs[0] else: return "en" # default language to user's preference, followed by # first app's default, followed by english language = request.couch_user.language or _default_lang() def _url_context(): # given a url path, returns potentially the app, parent, and case, if # they're selected. the front end optimizes with these to avoid excess # server calls # there's an annoying dependency between this logic and backbone's # url routing that seems hard to solve well. this needs to be synced # with apps.js if anything changes # for apps anything with "view/app/" works # for cases it will be: # "view/:app/:module/:form/case/:case/" # if there are parent cases, it will be: # "view/:app/:module/:form/parent/:parent/case/:case/ # could use regex here but this is actually simpler with the potential # absence of a trailing slash split = urlPath.split('/') app_id = split[1] if len(split) >= 2 else None if len(split) >= 5 and split[4] == "parent": parent_id = split[5] case_id = split[7] if len(split) >= 7 else None else: parent_id = None case_id = split[5] if len(split) >= 6 else None app = None if app_id: if app_id in [a['_id'] for a in apps]: app = look_up_app_json(domain, app_id) else: messages.info(request, _("That app is no longer valid. Try using the " "navigation links to select an app.")) if app is None and len(apps) == 1: app = look_up_app_json(domain, apps[0]['_id']) def _get_case(domain, case_id): case = CommCareCase.get(case_id) assert case.domain == domain, "case %s not in %s" % (case_id, domain) return case.get_json() case = _get_case(domain, case_id) if case_id else None if parent_id is None and case is not None: parent_id = case.get('indices',{}).get('parent', {}).get('case_id', None) parent = _get_case(domain, parent_id) if parent_id else None return { "app": app, "case": case, "parent": parent } context = { "domain": domain, "language": language, "apps": apps, "apps_raw": apps, "preview": preview, "maps_api_key": settings.GMAPS_API_KEY, 'offline_enabled': toggles.OFFLINE_CLOUDCARE.enabled(request.user.username), 'sessions_enabled': request.couch_user.is_commcare_user(), 'use_cloudcare_releases': request.project.use_cloudcare_releases, } context.update(_url_context()) return render(request, "cloudcare/cloudcare_home.html", context)
def get(self, request, domain, urlPath): try: preview = string_to_boolean(request.GET.get("preview", "false")) except ValueError: # this is typically only set at all if it's intended to be true so this # is a reasonable default for "something went wrong" preview = True app_access = ApplicationAccess.get_by_domain(domain) accessor = CaseAccessors(domain) if not preview: apps = get_cloudcare_apps(domain) if request.project.use_cloudcare_releases: if (toggles.CLOUDCARE_LATEST_BUILD.enabled(domain) or toggles.CLOUDCARE_LATEST_BUILD.enabled( request.couch_user.username)): get_cloudcare_app = get_latest_build_doc else: get_cloudcare_app = get_latest_released_app_doc apps = map( lambda app: get_cloudcare_app(domain, app['_id']), apps, ) apps = filter(None, apps) apps = map(wrap_app, apps) # convert to json apps = [get_app_json(app) for app in apps] else: # legacy functionality - use the latest build regardless of stars apps = [ get_latest_build_doc(domain, app['_id']) for app in apps ] apps = [ get_app_json(ApplicationBase.wrap(app)) for app in apps if app ] else: # big TODO: write a new apps view for Formplayer, can likely cut most out now if toggles.USE_FORMPLAYER_FRONTEND.enabled(domain): apps = get_cloudcare_apps(domain) else: apps = get_brief_apps_in_domain(domain) apps = [ get_app_json(app) for app in apps if app and (isinstance(app, RemoteApp) or app.application_version == V2) ] meta = get_meta(request) track_clicked_preview_on_hubspot(request.couch_user, request.COOKIES, meta) # trim out empty apps apps = filter(lambda app: app, apps) apps = filter( lambda app: app_access.user_can_access_app(request.couch_user, app ), apps) def _default_lang(): if apps: # unfortunately we have to go back to the DB to find this return Application.get(apps[0]["_id"]).default_language else: return "en" # default language to user's preference, followed by # first app's default, followed by english language = request.couch_user.language or _default_lang() def _url_context(): # given a url path, returns potentially the app, parent, and case, if # they're selected. the front end optimizes with these to avoid excess # server calls # there's an annoying dependency between this logic and backbone's # url routing that seems hard to solve well. this needs to be synced # with apps.js if anything changes # for apps anything with "view/app/" works # for cases it will be: # "view/:app/:module/:form/case/:case/" # if there are parent cases, it will be: # "view/:app/:module/:form/parent/:parent/case/:case/ # could use regex here but this is actually simpler with the potential # absence of a trailing slash split = urlPath.split('/') app_id = split[1] if len(split) >= 2 else None if len(split) >= 5 and split[4] == "parent": parent_id = split[5] case_id = split[7] if len(split) >= 7 else None else: parent_id = None case_id = split[5] if len(split) >= 6 else None app = None if app_id: if app_id in [a['_id'] for a in apps]: app = look_up_app_json(domain, app_id) else: messages.info( request, _("That app is no longer valid. Try using the " "navigation links to select an app.")) if app is None and len(apps) == 1: app = look_up_app_json(domain, apps[0]['_id']) def _get_case(domain, case_id): case = accessor.get_case(case_id) assert case.domain == domain, "case %s not in %s" % (case_id, domain) return case.to_api_json() case = _get_case(domain, case_id) if case_id else None if parent_id is None and case is not None: parent_id = case.get('indices', {}).get('parent', {}).get('case_id', None)
def filter_cases(request, domain, app_id, module_id, parent_id=None): app = Application.get(app_id) module = app.get_module(module_id) auth_cookie = request.COOKIES.get('sessionid') requires_parent_cases = string_to_boolean(request.GET.get('requires_parent_cases', 'false')) xpath = EntriesHelper.get_filter_xpath(module) instances = get_instances_for_module(app, module, additional_xpaths=[xpath]) extra_instances = [{'id': inst.id, 'src': inst.src} for inst in instances] accessor = CaseAccessors(domain) # touchforms doesn't like this to be escaped xpath = HTMLParser.HTMLParser().unescape(xpath) case_type = module.case_type if xpath or should_use_sql_backend(domain): # if we need to do a custom filter, send it to touchforms for processing additional_filters = { "properties/case_type": case_type, "footprint": True } helper = BaseSessionDataHelper(domain, request.couch_user) result = helper.filter_cases(xpath, additional_filters, DjangoAuth(auth_cookie), extra_instances=extra_instances) if result.get('status', None) == 'error': code = result.get('code', 500) message = result.get('message', _("Something went wrong filtering your cases.")) if code == 500: notify_exception(None, message=message) return json_response(message, status_code=code) case_ids = result.get("cases", []) else: # otherwise just use our built in api with the defaults case_ids = [res.id for res in get_filtered_cases( domain, status=CASE_STATUS_OPEN, case_type=case_type, user_id=request.couch_user._id, footprint=True, ids_only=True, )] cases = accessor.get_cases(case_ids) if parent_id: cases = filter(lambda c: c.parent and c.parent.case_id == parent_id, cases) # refilter these because we might have accidentally included footprint cases # in the results from touchforms. this is a little hacky but the easiest # (quick) workaround. should be revisted when we optimize the case list. cases = filter(lambda c: c.type == case_type, cases) cases = [c.to_api_json(lite=True) for c in cases if c] response = {'cases': cases} if requires_parent_cases: # Subtract already fetched cases from parent list parent_ids = set(map(lambda c: c['indices']['parent']['case_id'], cases)) - \ set(map(lambda c: c['case_id'], cases)) parents = accessor.get_cases(list(parent_ids)) parents = [c.to_api_json(lite=True) for c in parents] response.update({'parents': parents}) return json_response(response)
def to_boolean(val): return False if val == '' else string_to_boolean(val)
def get(self, request, domain, urlPath): try: preview = string_to_boolean(request.GET.get("preview", "false")) except ValueError: # this is typically only set at all if it's intended to be true so this # is a reasonable default for "something went wrong" preview = True app_access = ApplicationAccess.get_by_domain(domain) accessor = CaseAccessors(domain) if not preview: apps = get_cloudcare_apps(domain) if request.project.use_cloudcare_releases: if (toggles.CLOUDCARE_LATEST_BUILD.enabled(domain) or toggles.CLOUDCARE_LATEST_BUILD.enabled(request.couch_user.username)): get_cloudcare_app = get_latest_build_doc else: get_cloudcare_app = get_latest_released_app_doc apps = map( lambda app: get_cloudcare_app(domain, app['_id']), apps, ) apps = filter(None, apps) apps = map(wrap_app, apps) # convert to json apps = [get_app_json(app) for app in apps] else: # legacy functionality - use the latest build regardless of stars apps = [get_latest_build_doc(domain, app['_id']) for app in apps] apps = [get_app_json(ApplicationBase.wrap(app)) for app in apps if app] else: # big TODO: write a new apps view for Formplayer, can likely cut most out now if toggles.USE_FORMPLAYER_FRONTEND.enabled(domain): apps = get_cloudcare_apps(domain) else: apps = get_brief_apps_in_domain(domain) apps = [get_app_json(app) for app in apps if app and ( isinstance(app, RemoteApp) or app.application_version == V2)] meta = get_meta(request) track_clicked_preview_on_hubspot(request.couch_user, request.COOKIES, meta) # trim out empty apps apps = filter(lambda app: app, apps) apps = filter(lambda app: app_access.user_can_access_app(request.couch_user, app), apps) def _default_lang(): if apps: # unfortunately we have to go back to the DB to find this return Application.get(apps[0]["_id"]).default_language else: return "en" # default language to user's preference, followed by # first app's default, followed by english language = request.couch_user.language or _default_lang() def _url_context(): # given a url path, returns potentially the app, parent, and case, if # they're selected. the front end optimizes with these to avoid excess # server calls # there's an annoying dependency between this logic and backbone's # url routing that seems hard to solve well. this needs to be synced # with apps.js if anything changes # for apps anything with "view/app/" works # for cases it will be: # "view/:app/:module/:form/case/:case/" # if there are parent cases, it will be: # "view/:app/:module/:form/parent/:parent/case/:case/ # could use regex here but this is actually simpler with the potential # absence of a trailing slash split = urlPath.split('/') app_id = split[1] if len(split) >= 2 else None if len(split) >= 5 and split[4] == "parent": parent_id = split[5] case_id = split[7] if len(split) >= 7 else None else: parent_id = None case_id = split[5] if len(split) >= 6 else None app = None if app_id: if app_id in [a['_id'] for a in apps]: app = look_up_app_json(domain, app_id) else: messages.info(request, _("That app is no longer valid. Try using the " "navigation links to select an app.")) if app is None and len(apps) == 1: app = look_up_app_json(domain, apps[0]['_id']) def _get_case(domain, case_id): case = accessor.get_case(case_id) assert case.domain == domain, "case %s not in %s" % (case_id, domain) return case.to_api_json() case = _get_case(domain, case_id) if case_id else None if parent_id is None and case is not None: parent_id = case.get('indices', {}).get('parent', {}).get('case_id', None) parent = _get_case(domain, parent_id) if parent_id else None return { "app": app, "case": case, "parent": parent } context = { "domain": domain, "language": language, "apps": apps, "apps_raw": apps, "preview": preview, "maps_api_key": settings.GMAPS_API_KEY, "sessions_enabled": request.couch_user.is_commcare_user(), "use_cloudcare_releases": request.project.use_cloudcare_releases, "username": request.user.username, "formplayer_url": settings.FORMPLAYER_URL, 'use_sqlite_backend': use_sqlite_backend(domain), } context.update(_url_context()) if toggles.USE_FORMPLAYER_FRONTEND.enabled(domain): return render(request, "cloudcare/formplayer_home.html", context) else: return render(request, "cloudcare/cloudcare_home.html", context)
def create_or_update_users_and_groups(domain, user_specs, group_specs, task=None): from corehq.apps.users.views.mobile.custom_data_fields import UserFieldsView custom_data_validator = UserFieldsView.get_validator(domain) ret = {"errors": [], "rows": []} total = len(user_specs) + len(group_specs) def _set_progress(progress): if task is not None: DownloadBase.set_progress(task, progress, total) group_memoizer = create_or_update_groups(domain, group_specs, log=ret) current = len(group_specs) usernames = set() user_ids = set() allowed_groups = set(group_memoizer.groups) allowed_group_names = [group.name for group in allowed_groups] allowed_roles = UserRole.by_domain(domain) roles_by_name = {role.name: role for role in allowed_roles} can_assign_locations = domain_has_privilege(domain, privileges.LOCATIONS) if can_assign_locations: location_cache = SiteCodeToLocationCache(domain) domain_obj = Domain.get_by_name(domain) usernames_with_dupe_passwords = users_with_duplicate_passwords(user_specs) try: for row in user_specs: _set_progress(current) current += 1 data = row.get('data') email = row.get('email') group_names = list(map(six.text_type, row.get('group') or [])) language = row.get('language') name = row.get('name') password = row.get('password') phone_number = row.get('phone-number') uncategorized_data = row.get('uncategorized_data') user_id = row.get('user_id') username = row.get('username') location_codes = row.get('location_code') or [] if location_codes and not isinstance(location_codes, list): location_codes = [location_codes] # ignore empty location_codes = [code for code in location_codes if code] role = row.get('role', '') if password: password = six.text_type(password) try: username = normalize_username(six.text_type(username), domain) except TypeError: username = None except ValidationError: ret['rows'].append({ 'username': username, 'row': row, 'flag': _('username cannot contain spaces or symbols'), }) continue status_row = { 'username': raw_username(username) if username else None, 'row': row, } is_active = row.get('is_active') if isinstance(is_active, six.string_types): soft_assert_type_text(is_active) try: is_active = string_to_boolean(is_active) if is_active else None except ValueError: ret['rows'].append({ 'username': username, 'row': row, 'flag': _("'is_active' column can only contain 'true' or 'false'"), }) continue if username in usernames or user_id in user_ids: status_row['flag'] = 'repeat' elif not username and not user_id: status_row['flag'] = 'missing-data' else: try: if username: usernames.add(username) if user_id: user_ids.add(user_id) if user_id: user = CommCareUser.get_by_user_id(user_id, domain) else: user = CommCareUser.get_by_username(username) if domain_obj.strong_mobile_passwords and is_password(password): if raw_username(username) in usernames_with_dupe_passwords: raise UserUploadError(_("Provide a unique password for each mobile worker")) try: clean_password(password) except forms.ValidationError: if settings.ENABLE_DRACONIAN_SECURITY_FEATURES: msg = _("Mobile Worker passwords must be 8 " "characters long with at least 1 capital " "letter, 1 special character and 1 number") else: msg = _("Please provide a stronger password") raise UserUploadError(msg) if user: if user.domain != domain: raise UserUploadError(_( 'User with username %(username)r is ' 'somehow in domain %(domain)r' ) % {'username': user.username, 'domain': user.domain}) if username and user.username != username: raise UserUploadError(_( 'Changing usernames is not supported: %(username)r to %(new_username)r' ) % {'username': user.username, 'new_username': username}) if is_password(password): user.set_password(password) status_row['flag'] = 'updated' else: max_username_length = get_mobile_worker_max_username_length(domain) if len(raw_username(username)) > max_username_length: ret['rows'].append({ 'username': username, 'row': row, 'flag': _("username cannot contain greater than %d characters" % max_username_length) }) continue if not is_password(password): raise UserUploadError(_("Cannot create a new user with a blank password")) user = CommCareUser.create(domain, username, password, commit=False) status_row['flag'] = 'created' if phone_number: user.add_phone_number(_fmt_phone(phone_number), default=True) if name: user.set_full_name(six.text_type(name)) if data: error = custom_data_validator(data) if error: raise UserUploadError(error) user.user_data.update(data) if uncategorized_data: user.user_data.update(uncategorized_data) if language: user.language = language if email: try: validate_email(email) except ValidationError: raise UserUploadError(_("User has an invalid email address")) user.email = email.lower() if is_active is not None: user.is_active = is_active if can_assign_locations: # Do this here so that we validate the location code before we # save any other information to the user, this way either all of # the user's information is updated, or none of it location_ids = [] for code in location_codes: loc = get_location_from_site_code(code, location_cache) location_ids.append(loc.location_id) if role: if role in roles_by_name: user.set_role(domain, roles_by_name[role].get_qualified_id()) else: raise UserUploadError(_( "Role '%s' does not exist" ) % role) if can_assign_locations: locations_updated = set(user.assigned_location_ids) != set(location_ids) primary_location_removed = (user.location_id and not location_ids or user.location_id not in location_ids) if primary_location_removed: user.unset_location(commit=False) if locations_updated: user.reset_locations(location_ids, commit=False) user.save() if is_password(password): # Without this line, digest auth doesn't work. # With this line, digest auth works. # Other than that, I'm not sure what's going on # Passing use_primary_db=True because of https://dimagi-dev.atlassian.net/browse/ICDS-465 user.get_django_user(use_primary_db=True).check_password(password) for group_id in Group.by_user(user, wrap=False): group = group_memoizer.get(group_id) if group.name not in group_names: group.remove_user(user) for group_name in group_names: if group_name not in allowed_group_names: raise UserUploadError(_( "Can't add to group '%s' " "(try adding it to your spreadsheet)" ) % group_name) group_memoizer.by_name(group_name).add_user(user, save=False) except (UserUploadError, CouchUser.Inconsistent) as e: status_row['flag'] = six.text_type(e) ret["rows"].append(status_row) finally: try: group_memoizer.save_all() except BulkSaveError as e: _error_message = ( "Oops! We were not able to save some of your group changes. " "Please make sure no one else is editing your groups " "and try again." ) logging.exception(( 'BulkSaveError saving groups. ' 'User saw error message "%s". Errors: %s' ) % (_error_message, e.errors)) ret['errors'].append(_error_message) _set_progress(total) return ret
def create_or_update_users_and_groups(domain, user_specs, group_specs, location_specs, task=None): custom_data_validator = UserFieldsView.get_validator(domain) ret = {"errors": [], "rows": []} total = len(user_specs) + len(group_specs) + len(location_specs) def _set_progress(progress): if task is not None: DownloadBase.set_progress(task, progress, total) group_memoizer = create_or_update_groups(domain, group_specs, log=ret) current = len(group_specs) usernames = set() user_ids = set() allowed_groups = set(group_memoizer.groups) allowed_group_names = [group.name for group in allowed_groups] can_access_locations = domain_has_privilege(domain, privileges.LOCATIONS) if can_access_locations: location_cache = SiteCodeToLocationCache(domain) try: for row in user_specs: _set_progress(current) current += 1 data = row.get('data') email = row.get('email') group_names = map(unicode, row.get('group') or []) language = row.get('language') name = row.get('name') password = row.get('password') phone_number = row.get('phone-number') uncategorized_data = row.get('uncategorized_data') user_id = row.get('user_id') username = row.get('username') location_code = row.get('location-sms-code', '') if password: password = unicode(password) try: username = normalize_username(str(username), domain) except TypeError: username = None except ValidationError: ret['rows'].append({ 'username': username, 'row': row, 'flag': _('username cannot contain spaces or symbols'), }) continue status_row = { 'username': raw_username(username) if username else None, 'row': row, } is_active = row.get('is_active') if isinstance(is_active, basestring): try: is_active = string_to_boolean(is_active) except ValueError: ret['rows'].append({ 'username': username, 'row': row, 'flag': _("'is_active' column can only contain 'true' or 'false'"), }) continue if username in usernames or user_id in user_ids: status_row['flag'] = 'repeat' elif not username and not user_id: status_row['flag'] = 'missing-data' else: try: if username: usernames.add(username) if user_id: user_ids.add(user_id) if user_id: user = CommCareUser.get_by_user_id(user_id, domain) else: user = CommCareUser.get_by_username(username) def is_password(password): if not password: return False for c in password: if c != "*": return True return False if user: if user.domain != domain: raise UserUploadError(_( 'User with username %(username)r is ' 'somehow in domain %(domain)r' ) % {'username': user.username, 'domain': user.domain}) if username and user.username != username: user.change_username(username) if is_password(password): user.set_password(password) status_row['flag'] = 'updated' else: if len(raw_username(username)) > CommCareAccountForm.max_len_username: ret['rows'].append({ 'username': username, 'row': row, 'flag': _("username cannot contain greater than %d characters" % CommCareAccountForm.max_len_username) }) continue if not is_password(password): raise UserUploadError(_("Cannot create a new user with a blank password")) user = CommCareUser.create(domain, username, password, commit=False) status_row['flag'] = 'created' if phone_number: user.add_phone_number(_fmt_phone(phone_number), default=True) if name: user.set_full_name(name) if data: error = custom_data_validator(data) if error: raise UserUploadError(error) user.user_data.update(data) if uncategorized_data: user.user_data.update(uncategorized_data) if language: user.language = language if email: user.email = email if is_active is not None: user.is_active = is_active user.save() if can_access_locations and location_code: loc = location_cache.get(location_code) if user.location_id != loc._id: # this triggers a second user save so # we want to avoid doing it if it isn't # needed user.set_location(loc) if is_password(password): # Without this line, digest auth doesn't work. # With this line, digest auth works. # Other than that, I'm not sure what's going on user.get_django_user().check_password(password) for group_id in Group.by_user(user, wrap=False): group = group_memoizer.get(group_id) if group.name not in group_names: group.remove_user(user, save=False) for group_name in group_names: if group_name not in allowed_group_names: raise UserUploadError(_( "Can't add to group '%s' " "(try adding it to your spreadsheet)" ) % group_name) group_memoizer.by_name(group_name).add_user(user, save=False) except (UserUploadError, CouchUser.Inconsistent) as e: status_row['flag'] = unicode(e) ret["rows"].append(status_row) finally: try: group_memoizer.save_all() except BulkSaveError as e: _error_message = ( "Oops! We were not able to save some of your group changes. " "Please make sure no one else is editing your groups " "and try again." ) logging.exception(( 'BulkSaveError saving groups. ' 'User saw error message "%s". Errors: %s' ) % (_error_message, e.errors)) ret['errors'].append(_error_message) if Domain.get_by_name(domain).supports_multiple_locations_per_user: create_or_update_locations(domain, location_specs, log=ret) _set_progress(total) return ret
def create_or_update_users_and_groups(domain, user_specs, group_specs, location_specs, task=None): from corehq.apps.users.views.mobile.custom_data_fields import UserFieldsView custom_data_validator = UserFieldsView.get_validator(domain) ret = {"errors": [], "rows": []} total = len(user_specs) + len(group_specs) + len(location_specs) def _set_progress(progress): if task is not None: DownloadBase.set_progress(task, progress, total) group_memoizer = create_or_update_groups(domain, group_specs, log=ret) current = len(group_specs) usernames = set() user_ids = set() allowed_groups = set(group_memoizer.groups) allowed_group_names = [group.name for group in allowed_groups] can_access_locations = domain_has_privilege(domain, privileges.LOCATIONS) if can_access_locations: location_cache = SiteCodeToLocationCache(domain) try: for row in user_specs: _set_progress(current) current += 1 data = row.get('data') email = row.get('email') group_names = map(unicode, row.get('group') or []) language = row.get('language') name = row.get('name') password = row.get('password') phone_number = row.get('phone-number') uncategorized_data = row.get('uncategorized_data') user_id = row.get('user_id') username = row.get('username') location_code = row.get('location_code', '') if password: password = unicode(password) try: username = normalize_username(str(username), domain) except TypeError: username = None except ValidationError: ret['rows'].append({ 'username': username, 'row': row, 'flag': _('username cannot contain spaces or symbols'), }) continue status_row = { 'username': raw_username(username) if username else None, 'row': row, } is_active = row.get('is_active') if isinstance(is_active, basestring): try: is_active = string_to_boolean(is_active) if is_active else None except ValueError: ret['rows'].append({ 'username': username, 'row': row, 'flag': _("'is_active' column can only contain 'true' or 'false'"), }) continue if username in usernames or user_id in user_ids: status_row['flag'] = 'repeat' elif not username and not user_id: status_row['flag'] = 'missing-data' else: try: if username: usernames.add(username) if user_id: user_ids.add(user_id) if user_id: user = CommCareUser.get_by_user_id(user_id, domain) else: user = CommCareUser.get_by_username(username) def is_password(password): if not password: return False for c in password: if c != "*": return True return False if user: if user.domain != domain: raise UserUploadError(_( 'User with username %(username)r is ' 'somehow in domain %(domain)r' ) % {'username': user.username, 'domain': user.domain}) if username and user.username != username: user.change_username(username) if is_password(password): user.set_password(password) status_row['flag'] = 'updated' else: max_username_length = get_mobile_worker_max_username_length(domain) if len(raw_username(username)) > max_username_length: ret['rows'].append({ 'username': username, 'row': row, 'flag': _("username cannot contain greater than %d characters" % max_username_length) }) continue if not is_password(password): raise UserUploadError(_("Cannot create a new user with a blank password")) user = CommCareUser.create(domain, username, password, commit=False) status_row['flag'] = 'created' if phone_number: user.add_phone_number(_fmt_phone(phone_number), default=True) if name: user.set_full_name(unicode(name)) if data: error = custom_data_validator(data) if error: raise UserUploadError(error) user.user_data.update(data) if uncategorized_data: user.user_data.update(uncategorized_data) if language: user.language = language if email: email_validator = EmailValidator() try: email_validator(email) user.email = email except ValidationError: raise UserUploadError(_("User has an invalid email address")) if is_active is not None: user.is_active = is_active if can_access_locations: # Do this here so that we validate the location code before we # save any other information to the user, this way either all of # the user's information is updated, or none of it if location_code: loc = get_location_from_site_code(location_code, location_cache) else: loc = None user.save() if can_access_locations and loc: if user.location_id != loc.location_id: # this triggers a second user save so # we want to avoid doing it if it isn't # needed user.set_location(loc) if is_password(password): # Without this line, digest auth doesn't work. # With this line, digest auth works. # Other than that, I'm not sure what's going on user.get_django_user().check_password(password) for group_id in Group.by_user(user, wrap=False): group = group_memoizer.get(group_id) if group.name not in group_names: group.remove_user(user, save=False) for group_name in group_names: if group_name not in allowed_group_names: raise UserUploadError(_( "Can't add to group '%s' " "(try adding it to your spreadsheet)" ) % group_name) group_memoizer.by_name(group_name).add_user(user, save=False) except (UserUploadError, CouchUser.Inconsistent) as e: status_row['flag'] = unicode(e) ret["rows"].append(status_row) finally: try: group_memoizer.save_all() except BulkSaveError as e: _error_message = ( "Oops! We were not able to save some of your group changes. " "Please make sure no one else is editing your groups " "and try again." ) logging.exception(( 'BulkSaveError saving groups. ' 'User saw error message "%s". Errors: %s' ) % (_error_message, e.errors)) ret['errors'].append(_error_message) if Domain.get_by_name(domain).supports_multiple_locations_per_user: create_or_update_locations(domain, location_specs, log=ret) _set_progress(total) return ret
def filter_cases(request, domain, app_id, module_id, parent_id=None): app = Application.get(app_id) module = app.get_module(module_id) auth_cookie = request.COOKIES.get('sessionid') requires_parent_cases = string_to_boolean( request.GET.get('requires_parent_cases', 'false')) xpath = EntriesHelper.get_filter_xpath(module) instances = get_instances_for_module(app, module, additional_xpaths=[xpath]) extra_instances = [{'id': inst.id, 'src': inst.src} for inst in instances] use_formplayer = toggles.USE_FORMPLAYER.enabled(domain) accessor = CaseAccessors(domain) # touchforms doesn't like this to be escaped xpath = HTMLParser.HTMLParser().unescape(xpath) case_type = module.case_type if xpath or should_use_sql_backend(domain): # if we need to do a custom filter, send it to touchforms for processing additional_filters = { "properties/case_type": case_type, "footprint": True } helper = BaseSessionDataHelper(domain, request.couch_user) result = helper.filter_cases(xpath, additional_filters, DjangoAuth(auth_cookie), extra_instances=extra_instances, use_formplayer=use_formplayer) if result.get('status', None) == 'error': code = result.get('code', 500) message = result.get( 'message', _("Something went wrong filtering your cases.")) if code == 500: notify_exception(None, message=message) return json_response(message, status_code=code) case_ids = result.get("cases", []) else: # otherwise just use our built in api with the defaults case_ids = [ res.id for res in get_filtered_cases( domain, status=CASE_STATUS_OPEN, case_type=case_type, user_id=request.couch_user._id, footprint=True, ids_only=True, ) ] cases = accessor.get_cases(case_ids) if parent_id: cases = filter(lambda c: c.parent and c.parent.case_id == parent_id, cases) # refilter these because we might have accidentally included footprint cases # in the results from touchforms. this is a little hacky but the easiest # (quick) workaround. should be revisted when we optimize the case list. cases = filter(lambda c: c.type == case_type, cases) cases = [c.to_api_json(lite=True) for c in cases if c] response = {'cases': cases} if requires_parent_cases: # Subtract already fetched cases from parent list parent_ids = set(map(lambda c: c['indices']['parent']['case_id'], cases)) - \ set(map(lambda c: c['case_id'], cases)) parents = accessor.get_cases(list(parent_ids)) parents = [c.to_api_json(lite=True) for c in parents] response.update({'parents': parents}) return json_response(response)
return HttpResponseBadRequest("Must specify user_id!") ids_only = string_to_boolean(request.REQUEST.get("ids_only", "false")) case_id = request.REQUEST.get("case_id", "") if case_id: # short circuit everything else and just return the case # NOTE: this allows any user in the domain to access any case given # they know its ID, which is slightly different from the previous # behavior (can only access things you own + footprint). If we want to # change this contract we would need to update this to check the # owned case list + footprint case = CommCareCase.get(case_id) assert case.domain == domain cases = [CaseAPIResult(id=case_id, couch_doc=case, id_only=ids_only)] else: footprint = string_to_boolean(request.REQUEST.get( "footprint", "false")) filters = get_filters_from_request(request) status = api_closed_to_status(request.REQUEST.get('closed', 'false')) case_type = filters.get('properties/case_type', None) cases = get_filtered_cases(domain, status=status, case_type=case_type, user_id=user_id, filters=filters, footprint=footprint, ids_only=ids_only, strip_history=True) return json_response(cases) @cloudcare_api
def create_or_update_users_and_groups(domain, user_specs, group_specs, task=None): from corehq.apps.users.views.mobile.custom_data_fields import UserFieldsView custom_data_validator = UserFieldsView.get_validator(domain) ret = {"errors": [], "rows": []} total = len(user_specs) + len(group_specs) def _set_progress(progress): if task is not None: DownloadBase.set_progress(task, progress, total) group_memoizer = create_or_update_groups(domain, group_specs, log=ret) current = len(group_specs) usernames = set() user_ids = set() allowed_groups = set(group_memoizer.groups) allowed_group_names = [group.name for group in allowed_groups] allowed_roles = UserRole.by_domain(domain) roles_by_name = {role.name: role for role in allowed_roles} can_assign_locations = domain_has_privilege(domain, privileges.LOCATIONS) if can_assign_locations: location_cache = SiteCodeToLocationCache(domain) domain_obj = Domain.get_by_name(domain) usernames_with_dupe_passwords = users_with_duplicate_passwords(user_specs) try: for row in user_specs: _set_progress(current) current += 1 data = row.get('data') email = row.get('email') group_names = list(map(str, row.get('group') or [])) language = row.get('language') name = row.get('name') password = row.get('password') phone_number = row.get('phone-number') uncategorized_data = row.get('uncategorized_data') user_id = row.get('user_id') username = row.get('username') location_codes = row.get('location_code') or [] if location_codes and not isinstance(location_codes, list): location_codes = [location_codes] # ignore empty location_codes = [code for code in location_codes if code] role = row.get('role', '') if password: password = str(password) try: username = normalize_username(str(username), domain) except TypeError: username = None except ValidationError: ret['rows'].append({ 'username': username, 'row': row, 'flag': _('username cannot contain spaces or symbols'), }) continue status_row = { 'username': raw_username(username) if username else None, 'row': row, } is_active = row.get('is_active') if isinstance(is_active, str): try: is_active = string_to_boolean( is_active) if is_active else None except ValueError: ret['rows'].append({ 'username': username, 'row': row, 'flag': _("'is_active' column can only contain 'true' or 'false'" ), }) continue if username in usernames or user_id in user_ids: status_row['flag'] = 'repeat' elif not username and not user_id: status_row['flag'] = 'missing-data' else: try: if username: usernames.add(username) if user_id: user_ids.add(user_id) if user_id: user = CommCareUser.get_by_user_id(user_id, domain) else: user = CommCareUser.get_by_username(username) if domain_obj.strong_mobile_passwords and is_password( password): if raw_username( username) in usernames_with_dupe_passwords: raise UserUploadError( _("Provide a unique password for each mobile worker" )) try: clean_password(password) except forms.ValidationError: if settings.ENABLE_DRACONIAN_SECURITY_FEATURES: msg = _( "Mobile Worker passwords must be 8 " "characters long with at least 1 capital " "letter, 1 special character and 1 number") else: msg = _("Please provide a stronger password") raise UserUploadError(msg) if user: if user.domain != domain: raise UserUploadError( _('User with username %(username)r is ' 'somehow in domain %(domain)r') % { 'username': user.username, 'domain': user.domain }) if username and user.username != username: raise UserUploadError( _('Changing usernames is not supported: %(username)r to %(new_username)r' ) % { 'username': user.username, 'new_username': username }) if is_password(password): user.set_password(password) status_row['flag'] = 'updated' else: max_username_length = get_mobile_worker_max_username_length( domain) if len(raw_username(username)) > max_username_length: ret['rows'].append({ 'username': username, 'row': row, 'flag': _("username cannot contain greater than %d characters" % max_username_length) }) continue if not is_password(password): raise UserUploadError( _("Cannot create a new user with a blank password" )) user = CommCareUser.create(domain, username, password, commit=False) status_row['flag'] = 'created' if phone_number: user.add_phone_number(_fmt_phone(phone_number), default=True) if name: user.set_full_name(str(name)) if data: error = custom_data_validator(data) if error: raise UserUploadError(error) user.user_data.update(data) if uncategorized_data: user.user_data.update(uncategorized_data) if language: user.language = language if email: try: validate_email(email) except ValidationError: raise UserUploadError( _("User has an invalid email address")) user.email = email.lower() if is_active is not None: user.is_active = is_active if can_assign_locations: # Do this here so that we validate the location code before we # save any other information to the user, this way either all of # the user's information is updated, or none of it location_ids = [] for code in location_codes: loc = get_location_from_site_code( code, location_cache) location_ids.append(loc.location_id) if role: if role in roles_by_name: user.set_role( domain, roles_by_name[role].get_qualified_id()) else: raise UserUploadError( _("Role '%s' does not exist") % role) if can_assign_locations: locations_updated = set( user.assigned_location_ids) != set(location_ids) primary_location_removed = ( user.location_id and not location_ids or user.location_id not in location_ids) if primary_location_removed: user.unset_location(commit=False) if locations_updated: user.reset_locations(location_ids, commit=False) user.save() if is_password(password): # Without this line, digest auth doesn't work. # With this line, digest auth works. # Other than that, I'm not sure what's going on # Passing use_primary_db=True because of https://dimagi-dev.atlassian.net/browse/ICDS-465 user.get_django_user( use_primary_db=True).check_password(password) for group_id in Group.by_user_id(user.user_id, wrap=False): group = group_memoizer.get(group_id) if group.name not in group_names: group.remove_user(user) for group_name in group_names: if group_name not in allowed_group_names: raise UserUploadError( _("Can't add to group '%s' " "(try adding it to your spreadsheet)") % group_name) group_memoizer.by_name(group_name).add_user(user, save=False) except (UserUploadError, CouchUser.Inconsistent) as e: status_row['flag'] = str(e) ret["rows"].append(status_row) finally: try: group_memoizer.save_all() except BulkSaveError as e: _error_message = ( "Oops! We were not able to save some of your group changes. " "Please make sure no one else is editing your groups " "and try again.") logging.exception(('BulkSaveError saving groups. ' 'User saw error message "%s". Errors: %s') % (_error_message, e.errors)) ret['errors'].append(_error_message) _set_progress(total) return ret
def cloudcare_main(request, domain, urlPath): try: preview = string_to_boolean(request.REQUEST.get("preview", "false")) except ValueError: # this is typically only set at all if it's intended to be true so this # is a reasonable default for "something went wrong" preview = True app_access = ApplicationAccess.get_by_domain(domain) if not preview: apps = get_cloudcare_apps(domain) if request.project.use_cloudcare_releases: # replace the apps with the last starred build of each app, removing the ones that aren't starred apps = filter( lambda app: app.is_released, [get_app(domain, app['_id'], latest=True) for app in apps]) # convert to json apps = [get_app_json(app) for app in apps] else: # legacy functionality - use the latest build regardless of stars apps = [ get_app_json( ApplicationBase.get_latest_build(domain, app['_id'])) for app in apps ] else: apps = ApplicationBase.view('app_manager/applications_brief', startkey=[domain], endkey=[domain, {}]) apps = [ get_app_json(app) for app in apps if app and app.application_version == V2 ] # trim out empty apps apps = filter(lambda app: app, apps) apps = filter( lambda app: app_access.user_can_access_app(request.couch_user, app), apps) def _default_lang(): if apps: # unfortunately we have to go back to the DB to find this return Application.get(apps[0]["_id"]).build_langs[0] else: return "en" # default language to user's preference, followed by # first app's default, followed by english language = request.couch_user.language or _default_lang() def _url_context(): # given a url path, returns potentially the app, parent, and case, if # they're selected. the front end optimizes with these to avoid excess # server calls # there's an annoying dependency between this logic and backbone's # url routing that seems hard to solve well. this needs to be synced # with apps.js if anything changes # for apps anything with "view/app/" works # for cases it will be: # "view/:app/:module/:form/case/:case/" # if there are parent cases, it will be: # "view/:app/:module/:form/parent/:parent/case/:case/ # could use regex here but this is actually simpler with the potential # absence of a trailing slash split = urlPath.split('/') app_id = split[1] if len(split) >= 2 else None if len(split) >= 5 and split[4] == "parent": parent_id = split[5] case_id = split[7] if len(split) >= 7 else None else: parent_id = None case_id = split[5] if len(split) >= 6 else None app = None if app_id: if app_id in [a['_id'] for a in apps]: app = look_up_app_json(domain, app_id) else: messages.info( request, _("That app is no longer valid. Try using the " "navigation links to select an app.")) if app is None and len(apps) == 1: app = look_up_app_json(domain, apps[0]['_id']) def _get_case(domain, case_id): case = CommCareCase.get(case_id) assert case.domain == domain, "case %s not in %s" % (case_id, domain) return case.get_json() case = _get_case(domain, case_id) if case_id else None if parent_id is None and case is not None: parent_id = case.get('indices', {}).get('parent', {}).get('case_id', None)