Example #1
0
def get_new_multimedia_between_builds(domain, target_build_id, source_build_id, build_profile_id=None):
    def _get_mm_map_by_id(multimedia_map):
        return {
            media_map_item['multimedia_id']: media_map_item
            for path, media_map_item in
            multimedia_map.items()
        }

    source_build = get_app_cached(domain, source_build_id)
    target_build = get_app_cached(domain, target_build_id)
    assert source_build.copy_of, _("Size calculation available only for builds")
    assert target_build.copy_of, _("Size calculation available only for builds")
    build_profile = source_build.build_profiles.get(build_profile_id) if build_profile_id else None
    source_mm_map = source_build.multimedia_map_for_build(build_profile=build_profile)
    target_mm_map = target_build.multimedia_map_for_build(build_profile=build_profile)
    source_mm_map_by_id = _get_mm_map_by_id(source_mm_map)
    target_mm_map_by_id = _get_mm_map_by_id(target_mm_map)
    added = set(target_mm_map_by_id.keys()).difference(set(source_mm_map_by_id.keys()))
    media_objects = {
        mm.get_id: mm
        for path, mm in
        target_build.get_media_objects(multimedia_map=target_mm_map)
    }
    total_size = defaultdict(lambda: 0)
    for multimedia_id in added:
        media_object = media_objects[multimedia_id]
        total_size[media_object.doc_type] += media_object.content_length
    return total_size
Example #2
0
def registry_case(request, domain, app_id):
    request_dict = request.GET if request.method == 'GET' else request.POST
    case_ids = request_dict.getlist("case_id")
    case_types = request_dict.getlist("case_type")
    registry = request_dict.get(CASE_SEARCH_REGISTRY_ID_KEY)

    missing = [
        name for name, value in
        zip(["case_id", "case_type", CASE_SEARCH_REGISTRY_ID_KEY],
            [case_ids, case_types, registry]) if not value
    ]
    if missing:
        return HttpResponseBadRequest(
            ngettext("'{params}' is a required parameter",
                     "'{params}' are required parameters",
                     len(missing)).format(params="', '".join(missing)))

    helper = DataRegistryHelper(domain, registry_slug=registry)

    app = get_app_cached(domain, app_id)
    try:
        cases = [
            helper.get_case(case_id, request.couch_user, app)
            for case_id in case_ids
        ]
    except RegistryNotFound:
        return HttpResponseNotFound(f"Registry '{registry}' not found")
    except CaseNotFound as e:
        return HttpResponseNotFound(f"Case '{str(e)}' not found")
    except RegistryAccessException as e:
        return HttpResponseBadRequest(str(e))

    for case in cases:
        if case.type not in case_types:
            return HttpResponseNotFound(f"Case '{case.case_id}' not found")
Example #3
0
def heartbeat(request, domain, app_build_id):
    """
    An endpoint for CommCare mobile to get latest CommCare APK and app version
        info. (Should serve from cache as it's going to be busy view)

    'app_build_id' (that comes from URL) can be id of any version of the app
    'app_id' (urlparam) is usually id of an app that is not a copy
        mobile simply needs it to be resent back in the JSON, and doesn't
        need any validation on it. This is pulled from @uniqueid from profile.xml
    """
    app_id = request.GET.get('app_id', '')
    build_profile_id = request.GET.get('build_profile_id', '')
    master_app_id = app_id
    try:
        info = GlobalAppConfig.get_latest_version_info(domain, app_id, build_profile_id)
    except (Http404, AssertionError):
        # If it's not a valid master app id, find it by talking to couch
        app = get_app_cached(domain, app_build_id)
        notify_exception(request, 'Received an invalid heartbeat request')
        master_app_id = app.master_id if app else None
        info = GlobalAppConfig.get_latest_version_info(domain, app.master_id, build_profile_id)

    info["app_id"] = app_id
    if master_app_id:
        error_response = check_authorization(domain, request.couch_user, master_app_id)
        if error_response:
            return error_response
        if not toggles.SKIP_UPDATING_USER_REPORTING_METADATA.enabled(domain):
            update_user_reporting_data(app_build_id, app_id, build_profile_id, request.couch_user, request)

    if _should_force_log_submission(request):
        info['force_logs'] = True
    return JsonResponse(info)
Example #4
0
def heartbeat(request, domain, app_build_id):
    """
    An endpoint for CommCare mobile to get latest CommCare APK and app version
        info. (Should serve from cache as it's going to be busy view)

    'app_build_id' (that comes from URL) can be id of any version of the app
    'app_id' (urlparam) is usually id of an app that is not a copy
        mobile simply needs it to be resent back in the JSON, and doesn't
        need any validation on it. This is pulled from @uniqueid from profile.xml
    """
    app_id = request.GET.get('app_id', '')
    build_profile_id = request.GET.get('build_profile_id', '')

    info = {"app_id": app_id}
    try:
        # mobile will send brief_app_id
        info.update(LatestAppInfo(app_id, domain).get_info())
    except (Http404, AssertionError):
        # If it's not a valid 'brief' app id, find it by talking to couch
        notify_exception(request, 'Received an invalid heartbeat request')
        app = get_app_cached(domain, app_build_id)
        brief_app_id = app.master_id
        info.update(LatestAppInfo(brief_app_id, domain).get_info())

    else:
        if not toggles.SKIP_UPDATING_USER_REPORTING_METADATA.enabled(domain):
            update_user_reporting_data(app_build_id, app_id, build_profile_id,
                                       request.couch_user, request)
    if _should_force_log_submission(request):
        info['force_logs'] = True
    return JsonResponse(info)
Example #5
0
def heartbeat(request, domain, app_build_id):
    """
    An endpoint for CommCare mobile to get latest CommCare APK and app version
        info. (Should serve from cache as it's going to be busy view)

    'app_build_id' (that comes from URL) can be id of any version of the app
    'app_id' (urlparam) is usually id of an app that is not a copy
        mobile simply needs it to be resent back in the JSON, and doesn't
        need any validation on it. This is pulled from @uniqueid from profile.xml
    """
    app_id = request.GET.get('app_id', '')

    info = {"app_id": app_id}
    try:
        # mobile will send brief_app_id
        info.update(LatestAppInfo(app_id, domain).get_info())
    except (Http404, AssertionError):
        # If it's not a valid 'brief' app id, find it by talking to couch
        notify_exception(request, 'Received an invalid heartbeat request')
        app = get_app_cached(domain, app_build_id)
        brief_app_id = app.master_id
        info.update(LatestAppInfo(brief_app_id, domain).get_info())

    else:
        if settings.SERVER_ENVIRONMENT not in settings.ICDS_ENVS:
            # disable on icds for now since couch still not happy
            couch_user = request.couch_user
            try:
                update_user_reporting_data(app_build_id, app_id, couch_user, request)
            except ResourceConflict:
                # https://sentry.io/dimagi/commcarehq/issues/521967014/
                couch_user = CouchUser.get(couch_user.user_id)
                update_user_reporting_data(app_build_id, app_id, couch_user, request)

    return JsonResponse(info)
Example #6
0
def recovery_measures(request, domain, build_id):
    response = {
        "app_id": request.GET.get('app_id')
    }  # passed through unchanged
    app_id = get_app_cached(domain, build_id).master_id
    measures = get_recovery_measures_cached(domain, app_id)
    if measures:
        response["recovery_measures"] = measures
    return JsonResponse(response)
Example #7
0
def recovery_measures(request, domain, build_id):
    app_id = get_app_cached(domain, build_id).master_id
    response = {
        "latest_apk_version": get_default_build_spec().version,
        "latest_ccz_version": get_latest_released_app_version(domain, app_id),
        "app_id": request.GET.get('app_id'),  # passed through unchanged
    }
    measures = get_recovery_measures_cached(domain, app_id)
    if measures:
        response["recovery_measures"] = measures
    return JsonResponse(response)
Example #8
0
def recovery_measures(request, domain, build_id):
    app_id = get_app_cached(domain, build_id).master_id
    response = {
        "latest_apk_version": get_default_build_spec().version,
        "latest_ccz_version": get_latest_released_app_version(domain, app_id),
        "app_id": request.GET.get('app_id'),  # passed through unchanged
    }
    measures = get_recovery_measures_cached(domain, app_id)
    if measures:
        response["recovery_measures"] = measures
    return JsonResponse(response)
Example #9
0
def get_multimedia_sizes(request, domain, app_id, build_profile_id=None):
    """
    return size for different multimedia types and total for an app, directly presentable to the user
    """
    build = get_app_cached(domain, app_id)
    if not build.copy_of:
        return JsonResponse({
            "message": _("Multimedia size comparison is only available for app builds")
        }, status=400)
    mm_sizes = get_multimedia_sizes_for_build(build, build_profile_id=build_profile_id)
    if mm_sizes:
        mm_sizes = _update_mm_sizes(mm_sizes)
    return JsonResponse(mm_sizes)
Example #10
0
def get_related_cases(domain, app_id, case_type, cases):
    """
    Fetch related cases that are necessary to display any related-case
    properties in the app requesting this case search.

    Returns list of CommCareCase objects for adding to CaseDBFixture.
    """
    if not cases:
        return []

    app = get_app_cached(domain, app_id)
    paths = get_related_case_relationships(app, case_type)
    if not paths:
        return []
    return get_related_case_results(domain, cases, paths)
Example #11
0
def toggle_build_profile(request, domain, build_id, build_profile_id):
    build = get_app_cached(request.domain, build_id)
    status = request.GET.get('action') == 'enable'
    try:
        LatestEnabledBuildProfiles.update_status(build, build_profile_id, status)
    except ValidationError as e:
        messages.error(request, e)
    else:
        latest_enabled_build_profile = LatestEnabledBuildProfiles.for_app_and_profile(
            build.copy_of, build_profile_id)
        if latest_enabled_build_profile:
            messages.success(request, _("Latest version for profile {} is now {}").format(
                build.build_profiles[build_profile_id].name, latest_enabled_build_profile.version
            ))
        else:
            messages.success(request, _("Latest release now available for profile {}").format(
                build.build_profiles[build_profile_id].name
            ))
    return HttpResponseRedirect(reverse('download_index', args=[domain, build_id]))
Example #12
0
def get_multimedia_sizes_for_build(domain, build_id, build_profile_id=None):
    build = get_app_cached(domain, build_id)
    assert build.copy_of, _("Size calculation available only for builds")
    build_profile = build.build_profiles.get(build_profile_id) if build_profile_id else None
    multimedia_map_for_build = build.multimedia_map_for_build(build_profile=build_profile)
    multimedia_map_for_build_by_id = {
        media_map_item['multimedia_id']: media_map_item
        for path, media_map_item in
        multimedia_map_for_build.items()
    }
    media_objects = {
        mm_object.get_id: mm_object
        for path, mm_object in
        build.get_media_objects(multimedia_map=multimedia_map_for_build)
    }
    total_size = defaultdict(lambda: 0)
    for multimedia_id, media_item in multimedia_map_for_build_by_id.items():
        media_object = media_objects[multimedia_id]
        total_size[media_object.doc_type] += media_object.content_length
    return total_size
Example #13
0
def get_related_cases(helper, app_id, case_types, cases,
                      custom_related_case_property):
    """
    Fetch related cases that are necessary to display any related-case
    properties in the app requesting this case search.

    Returns list of CommCareCase objects for adding to CaseDBFixture.
    """
    if not cases:
        return []

    app = get_app_cached(helper.domain, app_id)
    paths = [
        rel for rels in [
            get_related_case_relationships(app, case_type)
            for case_type in case_types
        ] for rel in rels
    ]
    child_case_types = [
        _type for types in
        [get_child_case_types(app, case_type) for case_type in case_types]
        for _type in types
    ]

    expanded_case_results = []
    if custom_related_case_property:
        expanded_case_results.extend(
            get_expanded_case_results(helper, custom_related_case_property,
                                      cases))

    results = expanded_case_results
    top_level_cases = cases + expanded_case_results
    if paths:
        results.extend(get_related_case_results(helper, top_level_cases,
                                                paths))

    if child_case_types:
        results.extend(
            get_child_case_results(helper, top_level_cases, child_case_types))

    initial_case_ids = {case.case_id for case in cases}
Example #14
0
def heartbeat(request, domain, app_build_id):
    """
    An endpoint for CommCare mobile to get latest CommCare APK and app version
        info. (Should serve from cache as it's going to be busy view)

    'app_build_id' (that comes from URL) can be id of any version of the app
    'app_id' (urlparam) is usually id of an app that is not a copy
        mobile simply needs it to be resent back in the JSON, and doesn't
        need any validation on it. This is pulled from @uniqueid from profile.xml
    """
    app_id = request.GET.get('app_id', '')

    info = {"app_id": app_id}
    try:
        # mobile will send brief_app_id
        info.update(LatestAppInfo(app_id, domain).get_info())
    except (Http404, AssertionError):
        # If it's not a valid 'brief' app id, find it by talking to couch
        notify_exception(request, 'Received an invalid heartbeat request')
        app = get_app_cached(domain, app_build_id)
        brief_app_id = app.master_id
        info.update(LatestAppInfo(brief_app_id, domain).get_info())

    else:
        if settings.SERVER_ENVIRONMENT not in settings.ICDS_ENVS:
            # disable on icds for now since couch still not happy
            couch_user = request.couch_user
            try:
                update_user_reporting_data(app_build_id, app_id, couch_user,
                                           request)
            except ResourceConflict:
                # https://sentry.io/dimagi/commcarehq/issues/521967014/
                couch_user = CouchUser.get(couch_user.user_id)
                update_user_reporting_data(app_build_id, app_id, couch_user,
                                           request)

    if _should_force_log_submission(request):
        info['force_logs'] = True
    return JsonResponse(info)
Example #15
0
def get_restore_response(domain,
                         couch_user,
                         app_id=None,
                         since=None,
                         version='1.0',
                         state=None,
                         items=False,
                         force_cache=False,
                         cache_timeout=None,
                         overwrite_cache=False,
                         as_user=None,
                         device_id=None,
                         user_id=None,
                         openrosa_version=None,
                         case_sync=None):
    """
    :param domain: Domain being restored from
    :param couch_user: User performing restore
    :param app_id: App ID of the app making the request
    :param since: ID of current sync log used to generate incremental sync
    :param version: Version of the sync response required
    :param state: Hash value of the current database of cases on the device for consistency checking
    :param items: Include item count if True
    :param force_cache: Force response to be cached
    :param cache_timeout: Override the default cache timeout of 1 hour.
    :param overwrite_cache: Ignore cached response if True
    :param as_user: Username of user to generate restore for (if different from current user)
    :param device_id: ID of device performing restore
    :param user_id: ID of user performing restore (used in case of deleted user with same username)
    :param openrosa_version:
    :param case_sync: Override default case sync algorithm
    :return: Tuple of (http response, timing context or None)
    """

    if user_id and user_id != couch_user.user_id:
        # sync with a user that has been deleted but a new
        # user was created with the same username and password
        from couchforms.openrosa_response import get_simple_response_xml
        from couchforms.openrosa_response import ResponseNature
        response = get_simple_response_xml(
            'Attempt to sync with invalid user.',
            ResponseNature.OTA_RESTORE_ERROR)
        return HttpResponse(response,
                            content_type="text/xml; charset=utf-8",
                            status=412), None

    is_demo_restore = couch_user.is_commcare_user() and couch_user.is_demo_user
    if is_demo_restore:
        # if user is in demo-mode, return demo restore
        return demo_user_restore_response(couch_user), None

    uses_login_as = bool(as_user)
    as_user_obj = CouchUser.get_by_username(as_user) if uses_login_as else None
    if uses_login_as and not as_user_obj:
        msg = _('Invalid restore as user {}').format(as_user)
        return HttpResponse(msg, status=401), None
    is_permitted, message = is_permitted_to_restore(
        domain,
        couch_user,
        as_user_obj,
    )
    if not is_permitted:
        return HttpResponse(message, status=401), None

    restore_user = get_restore_user(domain, couch_user, as_user_obj)
    if not restore_user:
        return HttpResponse('Could not find user', status=404), None

    project = Domain.get_by_name(domain)
    async_restore_enabled = (toggles.ASYNC_RESTORE.enabled(domain)
                             and openrosa_version and
                             LooseVersion(openrosa_version) >= LooseVersion(
                                 OPENROSA_VERSION_MAP['ASYNC_RESTORE']))

    app = get_app_cached(domain, app_id) if app_id else None
    restore_config = RestoreConfig(
        project=project,
        restore_user=restore_user,
        params=RestoreParams(
            sync_log_id=since,
            version=version,
            state_hash=state,
            include_item_count=items,
            app=app,
            device_id=device_id,
            openrosa_version=openrosa_version,
        ),
        cache_settings=RestoreCacheSettings(force_cache=force_cache
                                            or async_restore_enabled,
                                            cache_timeout=cache_timeout,
                                            overwrite_cache=overwrite_cache),
        is_async=async_restore_enabled,
        case_sync=case_sync,
    )
    return restore_config.get_response(), restore_config.timing_context
Example #16
0
def get_restore_response(domain,
                         couch_user,
                         app_id=None,
                         since=None,
                         version='1.0',
                         state=None,
                         items=False,
                         force_cache=False,
                         cache_timeout=None,
                         overwrite_cache=False,
                         as_user=None,
                         device_id=None,
                         user_id=None,
                         openrosa_version=None,
                         case_sync=None):

    if user_id and user_id != couch_user.user_id:
        # sync with a user that has been deleted but a new
        # user was created with the same username and password
        from couchforms.openrosa_response import get_simple_response_xml
        from couchforms.openrosa_response import ResponseNature
        response = get_simple_response_xml(
            'Attempt to sync with invalid user.',
            ResponseNature.OTA_RESTORE_ERROR)
        return HttpResponse(response,
                            content_type="text/xml; charset=utf-8",
                            status=412), None

    is_demo_restore = couch_user.is_commcare_user() and couch_user.is_demo_user
    if is_demo_restore:
        # if user is in demo-mode, return demo restore
        return demo_user_restore_response(couch_user), None

    uses_login_as = bool(as_user)
    as_user_obj = CouchUser.get_by_username(as_user) if uses_login_as else None
    if uses_login_as and not as_user_obj:
        msg = _('Invalid restore as user {}').format(as_user)
        return HttpResponse(msg, status=401), None
    is_permitted, message = is_permitted_to_restore(
        domain,
        couch_user,
        as_user_obj,
    )
    if not is_permitted:
        return HttpResponse(message, status=401), None

    restore_user = get_restore_user(domain, couch_user, as_user_obj)
    if not restore_user:
        return HttpResponse('Could not find user', status=404), None

    project = Domain.get_by_name(domain)
    async_restore_enabled = (toggles.ASYNC_RESTORE.enabled(domain)
                             and openrosa_version and
                             LooseVersion(openrosa_version) >= LooseVersion(
                                 OPENROSA_VERSION_MAP['ASYNC_RESTORE']))

    app = get_app_cached(domain, app_id) if app_id else None
    restore_config = RestoreConfig(
        project=project,
        restore_user=restore_user,
        params=RestoreParams(
            sync_log_id=since,
            version=version,
            state_hash=state,
            include_item_count=items,
            app=app,
            device_id=device_id,
            openrosa_version=openrosa_version,
        ),
        cache_settings=RestoreCacheSettings(force_cache=force_cache
                                            or async_restore_enabled,
                                            cache_timeout=cache_timeout,
                                            overwrite_cache=overwrite_cache),
        async=async_restore_enabled,
        case_sync=case_sync,
    )
    return restore_config.get_response(), restore_config.timing_context
Example #17
0
 def test_get_app_cached(self):
     app_doc = get_app_cached(self.domain, self.v2_build.get_id)
     self.assertEqual(app_doc['is_released'], True)
Example #18
0
def get_restore_response(domain, couch_user, app_id=None, since=None, version='1.0',
                         state=None, items=False, force_cache=False,
                         cache_timeout=None, overwrite_cache=False,
                         as_user=None, device_id=None, user_id=None,
                         openrosa_version=None,
                         case_sync=None):
    """
    :param domain: Domain being restored from
    :param couch_user: User performing restore
    :param app_id: App ID of the app making the request
    :param since: ID of current sync log used to generate incremental sync
    :param version: Version of the sync response required
    :param state: Hash value of the current database of cases on the device for consistency checking
    :param items: Include item count if True
    :param force_cache: Force response to be cached
    :param cache_timeout: Override the default cache timeout of 1 hour.
    :param overwrite_cache: Ignore cached response if True
    :param as_user: Username of user to generate restore for (if different from current user)
    :param device_id: ID of device performing restore
    :param user_id: ID of user performing restore (used in case of deleted user with same username)
    :param openrosa_version:
    :param case_sync: Override default case sync algorithm
    :return: Tuple of (http response, timing context or None)
    """

    if user_id and user_id != couch_user.user_id:
        # sync with a user that has been deleted but a new
        # user was created with the same username and password
        from couchforms.openrosa_response import get_simple_response_xml
        from couchforms.openrosa_response import ResponseNature
        response = get_simple_response_xml(
            'Attempt to sync with invalid user.',
            ResponseNature.OTA_RESTORE_ERROR
        )
        return HttpResponse(response, content_type="text/xml; charset=utf-8", status=412), None

    is_demo_restore = couch_user.is_commcare_user() and couch_user.is_demo_user
    if is_demo_restore:
        # if user is in demo-mode, return demo restore
        return demo_user_restore_response(couch_user), None

    uses_login_as = bool(as_user)
    as_user_obj = CouchUser.get_by_username(as_user) if uses_login_as else None
    if uses_login_as and not as_user_obj:
        msg = _('Invalid restore as user {}').format(as_user)
        return HttpResponse(msg, status=401), None
    is_permitted, message = is_permitted_to_restore(
        domain,
        couch_user,
        as_user_obj,
    )
    if not is_permitted:
        return HttpResponse(message, status=401), None

    restore_user = get_restore_user(domain, couch_user, as_user_obj)
    if not restore_user:
        return HttpResponse('Could not find user', status=404), None

    project = Domain.get_by_name(domain)
    async_restore_enabled = (
        toggles.ASYNC_RESTORE.enabled(domain)
        and openrosa_version
        and LooseVersion(openrosa_version) >= LooseVersion(OPENROSA_VERSION_MAP['ASYNC_RESTORE'])
    )

    app = get_app_cached(domain, app_id) if app_id else None
    restore_config = RestoreConfig(
        project=project,
        restore_user=restore_user,
        params=RestoreParams(
            sync_log_id=since,
            version=version,
            state_hash=state,
            include_item_count=items,
            app=app,
            device_id=device_id,
            openrosa_version=openrosa_version,
        ),
        cache_settings=RestoreCacheSettings(
            force_cache=force_cache or async_restore_enabled,
            cache_timeout=cache_timeout,
            overwrite_cache=overwrite_cache
        ),
        is_async=async_restore_enabled,
        case_sync=case_sync,
    )
    return restore_config.get_response(), restore_config.timing_context