def page_context(self): """ This view services both domains that are master domains and domains that are linked domains (and legacy domains that are both). """ timezone = get_timezone_for_request() master_link = get_domain_master_link(self.domain) linked_domains = [self._link_context(link, timezone) for link in get_linked_domains(self.domain)] (master_apps, linked_apps) = self._get_apps() (master_reports, linked_reports) = self._get_reports() # Models belonging to this domain's master domain, for the purpose of pulling model_status = self._get_model_status(master_link, linked_apps, linked_reports) # Models belonging to this domain, for the purpose of pushing to linked domains master_model_status = self._get_master_model_status(master_apps, master_reports) return { 'domain': self.domain, 'timezone': timezone.localize(datetime.utcnow()).tzname(), 'is_linked_domain': bool(master_link), 'is_master_domain': bool(len(linked_domains)), 'view_data': { 'master_link': self._link_context(master_link, timezone) if master_link else None, 'model_status': sorted(model_status, key=lambda m: m['name']), 'master_model_status': sorted(master_model_status, key=lambda m: m['name']), 'linked_domains': linked_domains, 'models': [ {'slug': model[0], 'name': model[1]} for model in LINKED_MODELS ] }, }
def update_linked_model(self, in_data): model = in_data['model'] type_ = model['type'] detail = model['detail'] detail_obj = wrap_detail(type_, detail) if detail else None master_link = get_domain_master_link(self.domain) error = "" try: update_model_type(master_link, type_, detail_obj) model_detail = detail_obj.to_json() if detail_obj else None master_link.update_last_pull(type_, self.request.couch_user._id, model_detail=model_detail) except (DomainLinkError, UnsupportedActionError) as e: error = str(e) track_workflow(self.request.couch_user.username, "Linked domain: updated '{}' model".format(type_)) timezone = get_timezone_for_request() return { 'success': not error, 'error': error, 'last_update': server_to_user_time(master_link.last_pull, timezone) }
def release_domain(master_domain, linked_domain, username, models, build_apps=False): manager = ReleaseManager(master_domain, username) domain_link = get_domain_master_link(linked_domain) if not domain_link or domain_link.master_domain != master_domain: manager.add_error( linked_domain, _("Project space {} is no longer linked to {}. No content " "was released to it.").format(master_domain, linked_domain)) return manager.results() flag_dependent_models = { 'case_search_data': toggles.SYNC_SEARCH_CASE_CLAIM, 'data_dictionary': toggles.DATA_DICTIONARY, 'dialer_settings': toggles.WIDGET_DIALER, 'otp_settings': toggles.GAEN_OTP_SERVER, 'hmac_callout_settings': toggles.HMAC_CALLOUT } for model in models: errors = None try: if model['type'] == MODEL_APP: errors = manager._release_app(domain_link, model, manager.user, build_apps) elif model['type'] == MODEL_REPORT: errors = manager._release_report(domain_link, model) elif model['type'] in flag_dependent_models: errors = manager._release_flag_dependent_model( domain_link, model, manager.user, flag_dependent_models[model['type']]) elif model['type'] == MODEL_KEYWORD: errors = manager._release_keyword(domain_link, model) else: errors = manager._release_model(domain_link, model, manager.user) except Exception as e: # intentionally broad errors = [str(e), str(e)] notify_exception(None, "Exception pushing linked domains: {}".format(e)) if errors: manager.add_error(domain_link.linked_domain, _("Could not update {}: {}").format( model['name'], errors[0]), text=_("Could not update {}: {}").format( model['name'], errors[1])) else: manager.add_success( domain_link.linked_domain, _("Updated {} successfully").format(model['name'])) return manager.results()
def _inner(request, domain, *args, **kwargs): requester = request.META.get(REMOTE_REQUESTER_HEADER, None) if not requester: return HttpResponseBadRequest() link = get_domain_master_link(requester) if not link or link.master_domain != domain: return HttpResponseForbidden() return fn(request, domain, *args, **kwargs)
def release_domain(master_domain, linked_domain, username, models, build_apps=False): manager = ReleaseManager(master_domain, username) domain_link = get_domain_master_link(linked_domain) if not domain_link or domain_link.master_domain != master_domain: manager.add_error( linked_domain, _("Project space {} is no longer linked to {}. No content " "was released to it.").format(master_domain, linked_domain)) return manager.results() for model in models: errors = None try: if model['type'] == MODEL_APP: errors = manager._release_app(domain_link, model, manager.user, build_apps) elif model['type'] == MODEL_REPORT: errors = manager._release_report(domain_link, model) elif model['type'] in FEATURE_FLAG_DATA_MODEL_TOGGLES: errors = manager._release_flag_dependent_model( domain_link, model, manager.user, FEATURE_FLAG_DATA_MODEL_TOGGLES[model['type']]) elif model['type'] == MODEL_KEYWORD: errors = manager._release_keyword(domain_link, model) else: errors = manager._release_model(domain_link, model, manager.user) except Exception as e: # intentionally broad errors = [str(e), str(e)] notify_exception(None, "Exception pushing linked domains: {}".format(e)) if errors: manager.add_error(domain_link.linked_domain, _("Could not update {}: {}").format( model['name'], errors[0]), text=_("Could not update {}: {}").format( model['name'], errors[1])) else: manager.add_success( domain_link.linked_domain, _("Updated {} successfully").format(model['name'])) return manager.results()
def update_linked_model(self, in_data): model = in_data['model'] type_ = model['type'] detail = model['detail'] detail_obj = wrap_detail(type_, detail) if detail else None master_link = get_domain_master_link(self.domain) update_model_type(master_link, type_, detail_obj) master_link.update_last_pull(type_, self.request.couch_user._id, model_details=detail_obj) track_workflow(self.request.couch_user.username, "Linked domain: updated '{}' model".format(type_)) timezone = get_timezone_for_request() return { 'success': True, 'last_update': server_to_user_time(master_link.last_pull, timezone) }
def update_linked_model(self, in_data): model = in_data['model'] type_ = model['type'] detail = model['detail'] detail_obj = wrap_detail(type, detail) if detail else None master_link = get_domain_master_link(self.domain) update_model_type(master_link, type_, detail_obj) master_link.update_last_pull(type_, self.request.couch_user._id, model_details=detail_obj) track_workflow(self.request.couch_user.username, "Linked domain: updated '{}' model".format(type_)) timezone = get_timezone_for_request() return { 'success': True, 'last_update': server_to_user_time(master_link.last_pull, timezone) }
def master_link(self): return get_domain_master_link(self.domain)
def view_generic(request, domain, app_id, module_id=None, form_id=None, copy_app_form=None, release_manager=False, module_unique_id=None, form_unique_id=None): """ This is the main view for the app. All other views redirect to here. """ if form_id and not module_id and module_unique_id is None: return bail(request, domain, app_id) app = get_app(domain, app_id) module = form = None if module_id: try: module = app.get_module(module_id) except ModuleNotFoundException: raise Http404() if not module.unique_id: module.get_or_create_unique_id() app.save() elif module_unique_id: try: module = app.get_module_by_unique_id(module_unique_id) except ModuleNotFoundException: raise Http404() module_id = module.id if form_id and module is not None: try: form = module.get_form(form_id) except IndexError: raise Http404() elif form_unique_id: try: form = app.get_form(form_unique_id) except FormNotFoundException: raise Http404() form_id = form.id if form is not None and module is None: # this is the case where only the form_unique_id is given module = form.get_module() module_id = module.id # Application states that should no longer exist if app.application_version == APP_V1: _assert = soft_assert() _assert(False, 'App version 1.0', {'domain': domain, 'app_id': app_id}) return render(request, "app_manager/no_longer_supported.html", { 'domain': domain, 'app': app, }) if not app.vellum_case_management and not app.is_remote_app(): # Soft assert but then continue rendering; template will contain a user-facing warning _assert = soft_assert(['jschweers' + '@' + 'dimagi.com']) _assert(False, 'vellum_case_management=False', { 'domain': domain, 'app_id': app_id }) if (form is not None and "usercase_preload" in getattr(form, "actions", {}) and form.actions.usercase_preload.preload): _assert = soft_assert(['dmiller' + '@' + 'dimagi.com']) _assert( False, 'User property easy refs + old-style config = bad', { 'domain': domain, 'app_id': app_id, 'module_id': module_id, 'module_unique_id': module_unique_id, 'form_id': form_id, 'form_unique_id': form_unique_id, }) context = get_apps_base_context(request, domain, app) if app.copy_of: # redirect to "main" app rather than specific build return HttpResponseRedirect( reverse("view_app", args=[domain, app.copy_of])) context.update({ 'module': module, 'form': form, }) lang = context['lang'] if not module and hasattr(app, 'translations'): context.update({"translations": app.translations.get(lang, {})}) if not app.is_remote_app(): context.update({ 'add_ons': add_ons.get_dict(request, app, module, form), 'add_ons_privileges': add_ons.get_privileges_dict(request), 'add_ons_layout': add_ons.get_layout(request), }) if form: template, form_context = get_form_view_context_and_template( request, domain, form, context['langs'], current_lang=lang) context.update(form_context) elif module: template = get_module_template(request.user, module) # make sure all modules have unique ids app.ensure_module_unique_ids(should_save=True) module_context = get_module_view_context(request, app, module, lang) context.update(module_context) else: context.update(get_app_view_context(request, app)) template = 'app_manager/app_view_settings.html' if release_manager: template = 'app_manager/app_view_release_manager.html' if release_manager: context.update(get_releases_context(request, domain, app_id)) context.update({ 'is_app_settings_page': not release_manager, }) # update multimedia context for forms and modules. menu_host = form or module if menu_host: default_file_name = 'module%s' % module_id if form: default_file_name = '%s_form%s' % (default_file_name, form_id) specific_media = [{ 'menu_refs': app.get_menu_media(module, form=form, form_index=form_id, to_language=lang), 'default_file_name': '{name}_{lang}'.format(name=default_file_name, lang=lang), }] if not form and module and not isinstance( module, ReportModule) and module.uses_media(): def _make_name(suffix): return "{default_name}_{suffix}_{lang}".format( default_name=default_file_name, suffix=suffix, lang=lang, ) specific_media.append({ 'menu_refs': app.get_case_list_form_media(module, to_language=lang), 'default_file_name': _make_name('case_list_form'), 'qualifier': 'case_list_form_', }) specific_media.append({ 'menu_refs': app.get_case_list_menu_item_media(module, to_language=lang), 'default_file_name': _make_name('case_list_menu_item'), 'qualifier': 'case_list-menu_item_', }) if (toggles.CASE_LIST_LOOKUP.enabled(request.user.username) or toggles.CASE_LIST_LOOKUP.enabled(app.domain) or toggles.BIOMETRIC_INTEGRATION.enabled(app.domain)): specific_media.append({ 'menu_refs': app.get_case_list_lookup_image(module), 'default_file_name': '{}_case_list_lookup'.format(default_file_name), 'qualifier': 'case-list-lookupcase', }) if hasattr(module, 'product_details'): specific_media.append({ 'menu_refs': app.get_case_list_lookup_image(module, type='product'), 'default_file_name': '{}_product_list_lookup'.format(default_file_name), 'qualifier': 'case-list-lookupproduct', }) uploaders = { 'icon': MultimediaImageUploadController( "hqimage", reverse(ProcessImageFileUploadView.urlname, args=[app.domain, app.get_id])), 'audio': MultimediaAudioUploadController( "hqaudio", reverse(ProcessAudioFileUploadView.urlname, args=[app.domain, app.get_id])), } multimedia_map = app.multimedia_map if form or module: multimedia_map = (form or module).get_relevant_multimedia_map(app) context.update({ 'multimedia': { "object_map": app.get_object_map(multimedia_map=multimedia_map), 'upload_managers': uploaders, 'upload_managers_js': {type: u.js_options for type, u in uploaders.items()}, } }) context['module_icon'] = None if toggles.CUSTOM_ICON_BADGES.enabled(domain): context[ 'module_icon'] = module.custom_icon if module.custom_icon else CustomIcon( ) context['nav_menu_media_specifics'] = specific_media error = request.GET.get('error', '') context.update({ 'error': error, 'app': app, }) # Pass form for Copy Application to template domain_names = { d.name for d in Domain.active_for_user(request.couch_user) if not (is_linked_domain(request.domain) and get_domain_master_link( request.domain).master_domain == d.name) } domain_names.add(request.domain) if copy_app_form is None: copy_app_form = CopyApplicationForm(domain, app) context.update({ 'domain_names': sorted(domain_names), }) linked_domains_enabled = toggles.LINKED_DOMAINS.enabled(domain) context.update({ 'copy_app_form': copy_app_form, 'linked_domains_enabled': linked_domains_enabled, }) context['latest_commcare_version'] = get_commcare_versions( request.user)[-1] if not is_remote_app(app) and has_privilege( request, privileges.COMMCARE_LOGO_UPLOADER): uploader_slugs = list(ANDROID_LOGO_PROPERTY_MAPPING.keys()) from corehq.apps.hqmedia.controller import MultimediaLogoUploadController from corehq.apps.hqmedia.views import ProcessLogoFileUploadView uploaders = [ MultimediaLogoUploadController( slug, reverse( ProcessLogoFileUploadView.urlname, args=[domain, app_id, slug], )) for slug in uploader_slugs ] context.update({ "sessionid": request.COOKIES.get('sessionid'), "uploaders": uploaders, "uploaders_js": [u.js_options for u in uploaders], "refs": { slug: ApplicationMediaReference( app.logo_refs.get(slug, {}).get("path", slug), media_class=CommCareImage, ).as_dict() for slug in uploader_slugs }, "media_info": { slug: app.logo_refs.get(slug) for slug in uploader_slugs if app.logo_refs.get(slug) }, }) context.update({ 'show_live_preview': should_show_preview_app(request, app, request.couch_user.username), 'can_preview_form': request.couch_user.has_permission(domain, 'edit_data') }) confirm = request.session.pop('CONFIRM', False) context.update({'confirm': confirm}) response = render(request, template, context) set_lang_cookie(response, lang) return response
def view_generic(request, domain, app_id=None, module_id=None, form_id=None, copy_app_form=None, release_manager=False, module_unique_id=None, form_unique_id=None): """ This is the main view for the app. All other views redirect to here. """ if form_id and not module_id and module_unique_id is None: return bail(request, domain, app_id) app = module = form = None try: if app_id: app = get_app(domain, app_id) if module_id: try: module = app.get_module(module_id) except ModuleNotFoundException: raise Http404() if not module.unique_id: module.get_or_create_unique_id() app.save() elif module_unique_id: try: module = app.get_module_by_unique_id(module_unique_id) except ModuleNotFoundException: raise Http404() module_id = module.id if form_id and module is not None: try: form = module.get_form(form_id) except IndexError: raise Http404() elif form_unique_id: try: form = app.get_form(form_unique_id) except FormNotFoundException: raise Http404() form_id = form.id if form is not None and module is None: # this is the case where only the form_unique_id is given module = form.get_module() module_id = module.id except (ModuleNotFoundException, FormNotFoundException): return bail(request, domain, app_id) # Application states that should no longer exist if app: if app.application_version == APP_V1: _assert = soft_assert() _assert(False, 'App version 1.0', {'domain': domain, 'app_id': app_id}) return render(request, "app_manager/no_longer_supported.html", { 'domain': domain, 'app': app, }) if not app.vellum_case_management and not app.is_remote_app(): # Soft assert but then continue rendering; template will contain a user-facing warning _assert = soft_assert(['jschweers' + '@' + 'dimagi.com']) _assert(False, 'vellum_case_management=False', {'domain': domain, 'app_id': app_id}) if (form is not None and "usercase_preload" in getattr(form, "actions", {}) and form.actions.usercase_preload.preload): _assert = soft_assert(['dmiller' + '@' + 'dimagi.com']) _assert(False, 'User property easy refs + old-style config = bad', { 'domain': domain, 'app_id': app_id, 'module_id': module_id, 'module_unique_id': module_unique_id, 'form_id': form_id, 'form_unique_id': form_unique_id, }) context = get_apps_base_context(request, domain, app) if app and app.copy_of: # redirect to "main" app rather than specific build return HttpResponseRedirect(reverse( "view_app", args=[domain, app.copy_of] )) context.update({ 'module': module, 'form': form, }) lang = context['lang'] if app and not module and hasattr(app, 'translations'): context.update({"translations": app.translations.get(lang, {})}) if app and not app.is_remote_app(): context.update({ 'add_ons': add_ons.get_dict(request, app, module, form), 'add_ons_layout': add_ons.get_layout(request), }) if form: template, form_context = get_form_view_context_and_template( request, domain, form, context['langs'], current_lang=lang ) context.update(form_context) elif module: template = get_module_template(request.user, module) # make sure all modules have unique ids app.ensure_module_unique_ids(should_save=True) module_context = get_module_view_context(app, module, lang) context.update(module_context) elif app: context.update(get_app_view_context(request, app)) template = 'app_manager/app_view_settings.html' if release_manager: template = 'app_manager/app_view_release_manager.html' if release_manager: context.update(get_releases_context(request, domain, app_id)) context.update({ 'is_app_settings_page': not release_manager, }) else: from corehq.apps.dashboard.views import DomainDashboardView return HttpResponseRedirect(reverse(DomainDashboardView.urlname, args=[domain])) # update multimedia context for forms and modules. menu_host = form or module if menu_host: default_file_name = 'module%s' % module_id if form: default_file_name = '%s_form%s' % (default_file_name, form_id) specific_media = [{ 'menu_refs': app.get_menu_media( module, module_id, form=form, form_index=form_id, to_language=lang ), 'default_file_name': '{name}_{lang}'.format(name=default_file_name, lang=lang), }] if not form and module and not isinstance(module, ReportModule) and module.uses_media(): def _make_name(suffix): return "{default_name}_{suffix}_{lang}".format( default_name=default_file_name, suffix=suffix, lang=lang, ) specific_media.append({ 'menu_refs': app.get_case_list_form_media(module, module_id, to_language=lang), 'default_file_name': _make_name('case_list_form'), 'qualifier': 'case_list_form_', }) specific_media.append({ 'menu_refs': app.get_case_list_menu_item_media(module, module_id, to_language=lang), 'default_file_name': _make_name('case_list_menu_item'), 'qualifier': 'case_list-menu_item_', }) if (toggles.CASE_LIST_LOOKUP.enabled(request.user.username) or toggles.CASE_LIST_LOOKUP.enabled(app.domain)): specific_media.append({ 'menu_refs': app.get_case_list_lookup_image(module, module_id), 'default_file_name': '{}_case_list_lookup'.format(default_file_name), 'qualifier': 'case-list-lookupcase', }) if hasattr(module, 'product_details'): specific_media.append({ 'menu_refs': app.get_case_list_lookup_image(module, module_id, type='product'), 'default_file_name': '{}_product_list_lookup'.format(default_file_name), 'qualifier': 'case-list-lookupproduct', }) uploaders = { 'icon': MultimediaImageUploadController( "hqimage", reverse(ProcessImageFileUploadView.urlname, args=[app.domain, app.get_id]) ), 'audio': MultimediaAudioUploadController( "hqaudio", reverse(ProcessAudioFileUploadView.urlname, args=[app.domain, app.get_id]) ), } context.update({ 'multimedia': { "object_map": app.get_object_map(), 'upload_managers': uploaders, 'upload_managers_js': {type: u.js_options for type, u in six.iteritems(uploaders)}, } }) context['module_icon'] = None if toggles.CUSTOM_ICON_BADGES.enabled(domain): context['module_icon'] = module.custom_icon if module.custom_icon else CustomIcon() try: context['multimedia']['references'] = app.get_references() except ReportConfigurationNotFoundError: pass context['nav_menu_media_specifics'] = specific_media error = request.GET.get('error', '') context.update({ 'error': error, 'app': app, }) # Pass form for Copy Application to template domain_names = [ d.name for d in Domain.active_for_user(request.couch_user) if not (is_linked_domain(request.domain) and get_domain_master_link(request.domain).master_domain == d.name) ] domain_names.sort() if app and copy_app_form is None: toggle_enabled = toggles.EXPORT_ZIPPED_APPS.enabled(request.user.username) copy_app_form = CopyApplicationForm(domain, app, export_zipped_apps_enabled=toggle_enabled) context.update({ 'domain_names': domain_names, }) linked_domains_enabled = toggles.LINKED_DOMAINS.enabled(domain) context.update({ 'copy_app_form': copy_app_form, 'linked_domains_enabled': linked_domains_enabled, }) context['latest_commcare_version'] = get_commcare_versions(request.user)[-1] if (app and app.doc_type in ('Application', 'LinkedApplication') and has_privilege(request, privileges.COMMCARE_LOGO_UPLOADER)): uploader_slugs = list(ANDROID_LOGO_PROPERTY_MAPPING.keys()) from corehq.apps.hqmedia.controller import MultimediaLogoUploadController from corehq.apps.hqmedia.views import ProcessLogoFileUploadView uploaders = [ MultimediaLogoUploadController( slug, reverse( ProcessLogoFileUploadView.urlname, args=[domain, app_id, slug], ) ) for slug in uploader_slugs ] context.update({ "sessionid": request.COOKIES.get('sessionid'), "uploaders": uploaders, "uploaders_js": [u.js_options for u in uploaders], "refs": { slug: ApplicationMediaReference( app.logo_refs.get(slug, {}).get("path", slug), media_class=CommCareImage, module_id=app.logo_refs.get(slug, {}).get("m_id"), ).as_dict() for slug in uploader_slugs }, "media_info": { slug: app.logo_refs.get(slug) for slug in uploader_slugs if app.logo_refs.get(slug) }, }) context.update({ 'show_live_preview': app and should_show_preview_app( request, app, request.couch_user.username ), 'can_preview_form': request.couch_user.has_permission(domain, 'edit_data') }) confirm = request.session.pop('CONFIRM', False) context.update({'confirm': confirm}) response = render(request, template, context) response.set_cookie('lang', encode_if_unicode(lang)) return response
def can_view_exchange(request): return ( can_edit_apps(request) and not settings.ENTERPRISE_MODE and not get_domain_master_link(request.domain) # this isn't a linked domain )
def page_context(self): timezone = get_timezone_for_request() def _link_context(link, timezone=timezone): return { 'linked_domain': link.linked_domain, 'master_domain': link.master_domain, 'remote_base_url': link.remote_base_url, 'remote_username': link.remote_username, 'remote_api_key': link.remote_api_key, 'is_remote': link.is_remote, 'last_update': server_to_user_time(link.last_pull, timezone) if link.last_pull else 'Never', } model_status = [] linked_models = dict(LINKED_MODELS) master_link = get_domain_master_link(self.domain) if master_link: linked_apps = { app._id: app for app in get_brief_apps_in_domain(self.domain) if app.doc_type == 'LinkedApplication' } models_seen = set() history = DomainLinkHistory.objects.filter( link=master_link ).annotate(row_number=RawSQL( 'row_number() OVER (PARTITION BY model, model_detail ORDER BY date DESC)', [])) for action in history: models_seen.add(action.model) if action.row_number != 1: # first row is the most recent continue name = linked_models[action.model] update = { 'type': action.model, 'name': name, 'last_update': server_to_user_time(action.date, timezone), 'detail': action.model_detail, 'can_update': True } if action.model == 'app': app_name = 'Unknown App' if action.model_detail: detail = action.wrapped_detail app = linked_apps.pop(detail.app_id, None) app_name = app.name if app else detail.app_id if app: update['detail'] = action.model_detail else: update['can_update'] = False else: update['can_update'] = False update['name'] = '{} ({})'.format(name, app_name) model_status.append(update) # Add in models that have never been synced for model, name in LINKED_MODELS: if model not in models_seen and model != 'app': model_status.append({ 'type': model, 'name': name, 'last_update': ugettext('Never'), 'detail': None, 'can_update': True }) # Add in apps that have never been synced if linked_apps: for app in linked_apps.values(): update = { 'type': 'app', 'name': '{} ({})'.format(linked_models['app'], app.name), 'last_update': None, 'detail': AppLinkDetail(app_id=app._id).to_json(), 'can_update': True } model_status.append(update) return { 'domain': self.domain, 'timezone': timezone.localize(datetime.utcnow()).tzname(), 'view_data': { 'master_link': _link_context(master_link) if master_link else None, 'model_status': sorted(model_status, key=lambda m: m['name']), 'linked_domains': [ _link_context(link) for link in get_linked_domains(self.domain) ], 'models': [{ 'slug': model[0], 'name': model[1] } for model in LINKED_MODELS] }, }
def page_context(self): timezone = get_timezone_for_request() def _link_context(link, timezone=timezone): return { 'linked_domain': link.linked_domain, 'master_domain': link.qualified_master, 'remote_base_url': link.remote_base_url, 'is_remote': link.is_remote, 'last_update': server_to_user_time(link.last_pull, timezone) if link.last_pull else 'Never', } model_status = [] linked_models = dict(LINKED_MODELS) master_link = get_domain_master_link(self.domain) if master_link: linked_apps = { app._id: app for app in get_brief_apps_in_domain(self.domain) if app.doc_type == 'LinkedApplication' } models_seen = set() history = DomainLinkHistory.objects.filter(link=master_link).annotate(row_number=RawSQL( 'row_number() OVER (PARTITION BY model, model_detail ORDER BY date DESC)', [] )) for action in history: models_seen.add(action.model) if action.row_number != 1: # first row is the most recent continue name = linked_models[action.model] update = { 'type': action.model, 'name': name, 'last_update': server_to_user_time(action.date, timezone), 'detail': action.model_detail, 'can_update': True } if action.model == 'app': app_name = 'Unknown App' if action.model_detail: detail = action.wrapped_detail app = linked_apps.pop(detail.app_id, None) app_name = app.name if app else detail.app_id if app: update['detail'] = action.model_detail else: update['can_update'] = False else: update['can_update'] = False update['name'] = '{} ({})'.format(name, app_name) model_status.append(update) # Add in models that have never been synced for model, name in LINKED_MODELS: if model not in models_seen and model != 'app': model_status.append({ 'type': model, 'name': name, 'last_update': ugettext('Never'), 'detail': None, 'can_update': True }) # Add in apps that have never been synced if linked_apps: for app in linked_apps.values(): update = { 'type': 'app', 'name': '{} ({})'.format(linked_models['app'], app.name), 'last_update': None, 'detail': AppLinkDetail(app_id=app._id).to_json(), 'can_update': True } model_status.append(update) return { 'domain': self.domain, 'timezone': timezone.localize(datetime.utcnow()).tzname(), 'view_data': { 'master_link': _link_context(master_link) if master_link else None, 'model_status': sorted(model_status, key=lambda m: m['name']), 'linked_domains': [ _link_context(link) for link in get_linked_domains(self.domain) ], 'models': [ {'slug': model[0], 'name': model[1]} for model in LINKED_MODELS ] }, }