def create_item(request, app_label=None, model_name=None): """ This allows use to perform an inspection of the registered items so extensions don't need to register to get fancy creation wizards, they are available based on either the model name, or if that is ambiguous, present an option to make the right item. """ if not model_name: raise ImproperlyConfigured model_name = model_name.lower() mod = None if app_label is None: models = ContentType.objects.filter(app_label__in=fetch_metadata_apps()).filter(model=model_name) if models.count() == 0: raise Http404 # TODO: Throw better, more descriptive error elif models.count() == 1: mod = models.first().model_class() else: # models.count() > 1: # TODO: make this template return render(request, "aristotle_mdr/ambiguous_create_request.html", {'models': models}) else: try: mod = ContentType.objects.filter(app_label__in=fetch_metadata_apps()).get(app_label=app_label, model=model_name).model_class() except ObjectDoesNotExist: raise Http404 # TODO: Throw better, more descriptive error class DynamicAristotleWizard(ConceptWizard): model = mod return DynamicAristotleWizard.as_view()(request)
def check_concept_app_label(sender, instance, **kwargs): if not issubclass(sender, _concept): return if instance._meta.app_label not in fetch_metadata_apps(): raise ImproperlyConfigured(( "Trying to save item <{instance_name}> when app_label <{app_label}> " "is not enabled. Metadata apps at this point were <{metadata_apps}>." ).format(app_label=instance._meta.app_label, instance_name=instance.name, metadata_apps=str(fetch_metadata_apps())))
def get_queryset(self): app = self.get_app_label() if app not in fetch_metadata_apps(): raise Http404 app_config = get_app_config_list([app]) return add_urls_to_config_list(app_config, self.get_group())
def search(self): self.query_text = None kwargs = self.prepare_tokens() if not self.is_valid(): return self.no_query_found() if not self.cleaned_data.get('q'): return self.no_query_found() if self.query_text: sqs = self.searchqueryset.auto_query(self.query_text) else: sqs = self.searchqueryset if self.token_models: sqs = sqs.models(*self.token_models) if kwargs: sqs = sqs.filter(**kwargs) for word, boost_value in self.boost_ups: sqs = sqs.boost(word, boost_value) if self.load_all: sqs = sqs.load_all() # Only show models that are in apps that are enabled app_labels = fetch_metadata_apps() app_labels.append('aristotle_mdr_help') sqs = sqs.filter(django_ct_app_label__in=app_labels) return sqs
def handle_object_save(self, sender, instance, **kwargs): from aristotle_mdr.models import _concept, aristotleComponent itype = type(instance) # If saving a concept subclass if isinstance(instance, _concept) and itype is not _concept: if instance._meta.app_label in fetch_metadata_apps(): # If newly created if kwargs.get('created', False): # Make sure we have a _concept_ptr if hasattr(instance, '_concept_ptr'): concept = instance._concept_ptr ct = ContentType.objects.get_for_model(itype) concept._type = ct concept.save() # Handle async obj = instance.item self.async_handle_save(obj.__class__, obj, **kwargs) from aristotle_mdr.models import DiscussionPost if isinstance(instance, DiscussionPost): self.async_handle_save(type(instance), instance, **kwargs) # Components should have parents, but lets be kind. if issubclass(sender, aristotleComponent): parent_item = instance.parentItem if parent_item is not None: obj = parent_item.item self.async_handle_save(obj.__class__, obj, **kwargs)
def search(self): self.query_text = None kwargs = self.prepare_tokens() if not self.is_valid(): return self.no_query_found() if not self.cleaned_data.get('q'): return self.no_query_found() if self.query_text: sqs = self.searchqueryset.auto_query(self.query_text) else: sqs = self.searchqueryset if self.token_models: sqs = sqs.models(*self.token_models) if kwargs: sqs = sqs.filter(**kwargs) for word, boost_value in self.boost_ups: sqs = sqs.boost(word, boost_value) if self.load_all: sqs = sqs.load_all() # Only show models that are in apps that are enabled app_labels = fetch_metadata_apps() app_labels.append('aristotle_mdr_help') sqs = sqs.filter(django_ct_app_label__in=app_labels) return sqs
def admin_tools(request): if request.user.is_anonymous(): return redirect(reverse('friendly_login') + '?next=%s' % request.path) elif not request.user.has_perm("aristotle_mdr.access_aristotle_dashboard"): raise PermissionDenied aristotle_apps = fetch_metadata_apps() from django.contrib.contenttypes.models import ContentType models = ContentType.objects.filter(app_label__in=aristotle_apps).all() model_stats = {} for m in models: if m.model_class() and issubclass(m.model_class(), MDR._concept) and not m.model.startswith("_"): # Only output subclasses of 11179 concept app_models = model_stats.get(m.app_label, {'app': None, 'models': []}) if app_models['app'] is None: app_models['app'] = getattr(apps.get_app_config(m.app_label), 'verbose_name') app_models['models'].append( ( m.model_class(), get_cached_object_count(m), reverse("browse_concepts", args=[m.app_label, m.model]) ) ) model_stats[m.app_label] = app_models page = render( request, "aristotle_mdr/user/userAdminTools.html", {"item": request.user, "models": model_stats} ) return page
def public_standards(regAuth, itemType="aristotle_mdr._concept"): """ This is a filter that accepts a registration Authority and an item type and returns a list of tuples that contain all *public* items with a status of "Standard" or "Preferred Standard" *in that Registration Authority only*, as well as a the status object for that Authority. The item type should consist of the name of the app the item is from and the name of the item itself separated by a period (``.``). This requires the django ``django.contrib.contenttypes`` app is installed. If calling ``public_standards`` throws an exception or the item type requested is not found it safely returns an empty list. For example:: {% for item, status in registrationAuthority|public_standards:'aristotle_mdr.DataElement' %} {{ item }} - made standard on {{ status.registrationDate }}. {% endfor %} """ try: from django.contrib.contenttypes.models import ContentType app_label, model_name = itemType.lower().split('.', 1)[0:2] standard_states = [MDR.STATES.standard, MDR.STATES.preferred] return [(i, i.statuses.filter(registrationAuthority=regAuth, state__in=standard_states).first()) for i in ContentType.objects.filter( app_label__in=fetch_metadata_apps()).get( app_label=app_label, model=model_name).model_class(). objects.filter(statuses__registrationAuthority=regAuth, statuses__state__in=standard_states).public()] except: return []
def public_standards(regAuth, itemType="aristotle_mdr._concept"): """ This is a filter that accepts a registration Authority and an item type and returns a list of tuples that contain all *public* items with a status of "Standard" or "Preferred Standard" *in that Registration Authority only*, as well as a the status object for that Authority. The item type should consist of the name of the app the item is from and the name of the item itself separated by a period (``.``). This requires the django ``django.contrib.contenttypes`` app is installed. If calling ``public_standards`` throws an exception or the item type requested is not found it safely returns an empty list. For example:: {% for item, status in registrationAuthority|public_standards:'aristotle_mdr.DataElement' %} {{ item }} - made standard on {{ status.registrationDate }}. {% endfor %} """ try: from django.contrib.contenttypes.models import ContentType app_label, model_name=itemType.lower().split('.', 1)[0:2] standard_states = [MDR.STATES.standard, MDR.STATES.preferred] return [ (i, i.statuses.filter(registrationAuthority=regAuth, state__in=standard_states).first()) for i in ContentType.objects.filter(app_label__in=fetch_metadata_apps()).get(app_label=app_label, model=model_name).model_class().objects.filter(statuses__registrationAuthority=regAuth, statuses__state__in=standard_states).public() ] except: return []
def get_app_config_list(): out = {} aristotle_apps = fetch_metadata_apps() for m in get_concepts_for_apps(aristotle_apps): # Only output subclasses of 11179 concept app_models = out.get(m.app_label, {'app': None, 'models': []}) if app_models['app'] is None: try: app_models['app'] = apps.get_app_config(m.app_label) except: # Where no name is configured in the app_config, set a dummy so we don't keep trying from aristotle_mdr.apps import AristotleExtensionBaseConfig app = AristotleExtensionBaseConfig() app.verbose_name = "No name" app_models['app'] = app app_models['models'].append({ "content_type": m, "class": m.model_class() }) out[m.app_label] = app_models return sorted(out.values(), key=lambda x: (x['app'].create_page_priority, x['app']. create_page_name, x['app'].verbose_name))
def get_context_data(self, *args, **kwargs): # Call the base implementation first to get a context context = super().get_context_data(*args, **kwargs) if self.kwargs['app'] not in fetch_metadata_apps(): raise Http404 context['app_label'] = self.kwargs['app'] context['app'] = apps.get_app_config(self.kwargs['app']) return context
def get_object(self, queryset=None): if self.kwargs['app'] not in fetch_metadata_apps(): raise Http404 app = self.kwargs['app'] model = self.kwargs['model'] return get_object_or_404(ConceptHelp, app_label=app, concept_type=model)
def get_context_data(self, **kwargs): # Call the base implementation first to get a context context = super().get_context_data(**kwargs) app = self.get_app_label() if app not in fetch_metadata_apps(): raise Http404 context['app_label'] = app context['app'] = apps.get_app_config(app) return context
def check_concept_app_label(sender, instance, **kwargs): if not issubclass(sender, _concept): return if instance._meta.app_label not in fetch_metadata_apps(): raise ImproperlyConfigured( "Trying to save item <{instance_name}> when app_label <{app_label}> is not enabled".format( app_label=instance._meta.app_label, instance_name=instance.name ) )
def model(self): if self.kwargs['app'] not in fetch_metadata_apps(): raise Http404 if self._model is None: app = self.kwargs['app'] model = self.kwargs['model'] ct = ContentType.objects.filter(app_label=app, model=model) if not ct: raise Http404 else: self._model = ct.first().model_class() return self._model
def __init__(self, *args, **kwargs): if 'searchqueryset' not in kwargs.keys() or kwargs['searchqueryset'] is None: kwargs['searchqueryset'] = get_permission_sqs() if not issubclass(type(kwargs['searchqueryset']), PermissionSearchQuerySet): raise ImproperlyConfigured("Aristotle Search Queryset connection must be a subclass of PermissionSearchQuerySet") super().__init__(*args, **kwargs) self.fields['ra'].choices = [(ra.id, ra.name) for ra in MDR.RegistrationAuthority.objects.all()] self.fields['models'].choices = [ m for m in model_choices() if m[0].split('.', 1)[0] in fetch_metadata_apps() + ['aristotle_mdr_help'] ]
def handle_concept_save(self, sender, instance, **kwargs): from aristotle_mdr.models import _concept, aristotleComponent if isinstance(instance, _concept) and type(instance) is not _concept: if instance._meta.app_label in fetch_metadata_apps(): obj = instance.item self.async_handle_save(obj.__class__, obj, **kwargs) else: return # Components should have parents, but lets be kind. if issubclass(sender, aristotleComponent) and hasattr(instance, "parentItem"): obj = instance.parentItem.item self.async_handle_save(obj.__class__, obj, **kwargs)
def handle_concept_save(self, sender, instance, **kwargs): from aristotle_mdr.models import _concept, aristotleComponent if isinstance(instance, _concept) and type(instance) is not _concept: if instance._meta.app_label in fetch_metadata_apps(): obj = instance.item self.async_handle_save(obj.__class__, obj, **kwargs) else: return # Components should have parents, but lets be kind. if issubclass(sender, aristotleComponent) and hasattr( instance, "parentItem"): obj = instance.parentItem.item self.async_handle_save(obj.__class__, obj, **kwargs)
def model(self): if self.get_app_label() not in fetch_metadata_apps(): logger.critical(self.get_app_label()) raise Http404 if self._model is None: app = self.get_app_label() model = self.get_model_name() ct = ContentType.objects.filter(app_label=app, model=model) if not ct: raise Http404 else: self._model = ct.first().model_class() if not issubclass(self._model, _concept): raise Http404 return self._model
def __init__(self, *args, **kwargs): if 'searchqueryset' not in kwargs.keys() or kwargs['searchqueryset'] is None: kwargs['searchqueryset'] = get_permission_sqs() if not issubclass(type(kwargs['searchqueryset']), PermissionSearchQuerySet): raise ImproperlyConfigured("Aristotle Search Queryset connection must be a subclass of PermissionSearchQuerySet") super().__init__(*args, **kwargs) # Show visible workgroups ordered by active state and name # Inactive last self.fields['ra'].choices = [(ra.id, ra.name) for ra in MDR.RegistrationAuthority.objects.filter(active__in=[0, 1]).order_by('active', 'name')] self.fields['models'].choices = [ m for m in model_choices() if m[0].split('.', 1)[0] in fetch_metadata_apps() + ['aristotle_mdr_help'] ]
def test_help_not_visible_if_disabled(self): self.setup_help() self.assertFalse('extension_test' in fetch_metadata_apps()) response = self.client.get(reverse('aristotle_help:help_base')) self.assertNotContains(response, "My very specific help page") response = self.client.get(reverse('aristotle_help:help_concepts')) self.assertNotContains(response, "Question") response = self.client.get(reverse('aristotle_help:concept_app_help', args=['extension_test'])) self.assertTrue(response.status_code == 404) response = self.client.get(reverse('aristotle_help:concept_help', args=['extension_test', 'question'])) self.assertTrue(response.status_code == 404)
def __init__(self, *args, **kwargs): if 'searchqueryset' not in kwargs.keys() or kwargs['searchqueryset'] is None: kwargs['searchqueryset'] = get_permission_sqs() if not issubclass(type(kwargs['searchqueryset']), PermissionSearchQuerySet): raise ImproperlyConfigured("Aristotle Search Queryset connection must be a subclass of PermissionSearchQuerySet") super().__init__(*args, **kwargs) # Show visible workgroups ordered by active state and name # Inactive last self.fields['ra'].choices = [(ra.id, ra.name) for ra in MDR.RegistrationAuthority.objects.filter(active__in=[0, 1]).order_by('active', 'name')] self.fields['models'].choices = [ m for m in model_choices() if m[0].split('.', 1)[0] in fetch_metadata_apps() + ['aristotle_mdr_help'] ]
def prepare_tokens(self): try: query = self.cleaned_data.get('q') except: return {} opts = connections[DEFAULT_ALIAS].get_unified_index().fields.keys() kwargs = {} query_text = [] token_models = [] for word in query.split(" "): if ":" in word: opt, arg = word.split(":", 1) if opt in self.token_shortnames: opt = self.token_shortnames[opt] if opt in opts and opt in self.allowed_tokens: kwargs[str(opt)] = arg elif opt == "type": # we'll allow these through and assume they meant content type from django.contrib.contenttypes.models import ContentType arg = arg.lower().replace('_', '').replace('-', '') app_labels = fetch_metadata_apps() app_labels.append('aristotle_mdr_help') mods = ContentType.objects.filter( app_label__in=app_labels).all() for i in mods: if hasattr(i.model_class(), 'get_verbose_name'): model_short_code = "".join( map( first_letter, i.model_class()._meta.verbose_name.split( " "))).lower() if arg == model_short_code: token_models.append(i.model_class()) if arg == i.model: token_models.append(i.model_class()) else: query_text.append(word) self.token_models = token_models self.query_text = " ".join(query_text) self.kwargs = kwargs return kwargs
def test_help_not_visible_if_disabled(self): self.setup_help() self.assertFalse('extension_test' in fetch_metadata_apps()) response = self.client.get(reverse('aristotle_help:help_base')) self.assertNotContains(response, "My very specific help page") response = self.client.get(reverse('aristotle_help:help_concepts')) self.assertNotContains(response, "Question") response = self.client.get( reverse('aristotle_help:concept_app_help', args=['extension_test'])) self.assertTrue(response.status_code == 404) response = self.client.get( reverse('aristotle_help:concept_help', args=['extension_test', 'question'])) self.assertTrue(response.status_code == 404)
def get_context_data(self, **kwargs): app = self.kwargs['app'] model = self.kwargs['model'] field_name = self.kwargs['field_name'] ct = get_object_or_404(ContentType, app_label=app, model=model) field = None if app not in fetch_metadata_apps(): raise Http404 try: field = ct.model_class()._meta.get_field(field_name) except FieldDoesNotExist: try: field = ct.model_class()._meta.get_field(field_name + "_set") except FieldDoesNotExist: pass kwargs.update({'field': field}) self.cloud_help_handler(ct, field_name, kwargs) if not field: # If the passed field name is not the model's field name, then this must be a CustomField... from aristotle_mdr.contrib.custom_fields.models import CustomField try: field = CustomField.objects.get(allowed_model=ct, name__iexact=field_name) except ObjectDoesNotExist: raise Http404 kwargs.update({ 'custom_field': field, 'field': field_name, 'this_is_a_custom_field': True, }) kwargs.update({ 'app': apps.get_app_config(app), 'model_name': model, 'model': ct.model_class(), 'content_type': ct, }) return super().get_context_data(**kwargs)
def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) aristotle_apps = fetch_metadata_apps() out = {} for m in get_concepts_for_apps(aristotle_apps): # Only output subclasses of 11179 concept app_models = out.get(m.app_label, {'app': None, 'models': []}) if app_models['app'] is None: app_models['app'] = getattr( apps.get_app_config(m.app_label), 'verbose_name', _("No name") # Where no name is configured in the app_config, set a dummy so we don't keep trying ) app_models['models'].append(m) out[m.app_label] = app_models context['apps'] = OrderedDict(sorted(out.items(), key=lambda app: app[1]['app'])) return context
def prepare_tokens(self): try: query = self.cleaned_data.get('q') except: return {} opts = connections[DEFAULT_ALIAS].get_unified_index().fields.keys() kwargs = {} query_text = [] token_models = [] boost_ups = [] for word in query.split(" "): if word.startswith("+"): boost_strength = min(4, len(word) - len(word.lstrip('+'))) boost_val = round(0.1 + 1.1 ** (boost_strength ** 1.35), 3) query_text.append(word) boost_ups.append((word.lstrip("+"), boost_val)) continue word = word.replace("+", " ") if ":" in word: opt, arg = word.split(":", 1) if opt in self.token_shortnames: opt = self.token_shortnames[opt] if opt in opts and opt in self.allowed_tokens: clean_arguments_func = getattr(self, "process_%s" % opt, None) if not clean_arguments_func: kwargs[str(opt)]=arg else: # if we have a processor, run that. clean_value = clean_arguments_func(arg) if type(clean_value) is list: kwargs["%s__in" % str(opt)] = clean_value elif clean_value is not None: kwargs[str(opt)] = clean_value elif opt == "type": # we'll allow these through and assume they meant content type from django.contrib.contenttypes.models import ContentType arg = arg.lower().replace('_', '').replace('-', '') app_labels = fetch_metadata_apps() app_labels.append('aristotle_mdr_help') mods = ContentType.objects.filter(app_label__in=app_labels).all() for i in mods: if hasattr(i.model_class(), 'get_verbose_name'): model_short_code = "".join( map( first_letter, i.model_class()._meta.verbose_name.split(" ") ) ).lower() if arg == model_short_code: token_models.append(i.model_class()) if arg == i.model: token_models.append(i.model_class()) else: query_text.append(word) self.token_models = token_models self.query_text = " ".join(query_text) self.kwargs = kwargs self.boost_ups = boost_ups return kwargs
def get_queryset(self): return ConceptHelp.objects.filter( Q(app_label__in=fetch_metadata_apps()) | Q(app_label__isnull=True) )
def admin_stats(request): if request.user.is_anonymous(): return redirect(reverse('friendly_login') + '?next=%s' % request.path) elif not request.user.has_perm("aristotle_mdr.access_aristotle_dashboard"): raise PermissionDenied aristotle_apps = fetch_metadata_apps() from django.contrib.contenttypes.models import ContentType models = ContentType.objects.filter(app_label__in=aristotle_apps).all() model_stats = {} # Get datetime objects for '7 days ago' and '30 days ago' t7 = datetime.date.today() - datetime.timedelta(days=7) t30 = datetime.date.today() - datetime.timedelta(days=30) mod_counts = [] # used to get the maximum count use_cache = True # We still cache but its much, much shorter for m in models: if m.model_class() and issubclass(m.model_class(), MDR._concept) and not m.model.startswith("_"): # Only output subclasses of 11179 concept app_models = model_stats.get(m.app_label, {'app': None, 'models': []}) if app_models['app'] is None: app_models['app'] = getattr(apps.get_app_config(m.app_label), 'verbose_name') if use_cache: total = get_cached_query_count( qs=m.model_class().objects, key=model_to_cache_key(m) + "__all_time", ttl=60 ) t7_val = get_cached_query_count( qs=m.model_class().objects.filter(created__gte=t7), key=model_to_cache_key(m) + "__t7", ttl=60 ) t30_val = get_cached_query_count( qs=m.model_class().objects.filter(created__gte=t30), key=model_to_cache_key(m) + "__t30", ttl=60 ) else: total = m.model_class().objects.count() t7_val = m.model_class().objects.filter(created__gte=t7).count() t30_val = m.model_class().objects.filter(created__gte=t30).count() mod_counts.append(total) app_models['models'].append( ( m.model_class(), { 'all_time': total, 't7': t7_val, 't30': t30_val }, reverse("browse_concepts", args=[m.app_label, m.model]) ) ) model_stats[m.app_label] = app_models page = render( request, "aristotle_mdr/user/userAdminStats.html", {"item": request.user, "model_stats": model_stats, 'model_max': max(mod_counts)} ) return page
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['app'] = apps.get_app_config(self.kwargs['app']) if self.kwargs['app'] not in fetch_metadata_apps(): raise Http404 return context
def get_queryset(self, *args, **kwargs): return ConceptHelp.objects.filter( app_label__in=fetch_metadata_apps() ).filter(app_label=self.kwargs['app'])
def get_queryset(self, *args, **kwargs): return ConceptHelp.objects.filter( app_label__in=fetch_metadata_apps()).filter( app_label=self.kwargs['app'])
def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['app'] = apps.get_app_config(self.kwargs['app']) if self.kwargs['app'] not in fetch_metadata_apps(): raise Http404 return context
def check_app(self, item): label = type(item)._meta.app_label if label not in fetch_metadata_apps(): return False return True
def check_app(self, item): label = type(item)._meta.app_label if label not in fetch_metadata_apps(): return False return True
def get_object(self, queryset=None): if self.kwargs['app'] not in fetch_metadata_apps(): raise Http404 app = self.kwargs['app'] model = self.kwargs['model'] return get_object_or_404(ConceptHelp, app_label=app, concept_type=model)
def prepare_tokens(self): try: query = self.cleaned_data.get('q') except: return {} opts = connections[DEFAULT_ALIAS].get_unified_index().fields.keys() kwargs = {} query_text = [] token_models = [] boost_ups = [] for word in query.split(" "): if word.startswith("+"): boost_strength = min(4, len(word) - len(word.lstrip('+'))) boost_val = round(0.1 + 1.1 ** (boost_strength ** 1.35), 3) query_text.append(word) boost_ups.append((word.lstrip("+"), boost_val)) continue word = word.replace("+", " ") if ":" in word: opt, arg = word.split(":", 1) if opt in self.token_shortnames: opt = self.token_shortnames[opt] if opt in opts and opt in self.allowed_tokens: clean_arguments_func = getattr(self, "process_%s" % opt, None) if not clean_arguments_func: kwargs[str(opt)]=arg else: # if we have a processor, run that. clean_value = clean_arguments_func(arg) if type(clean_value) is list: kwargs["%s__in" % str(opt)] = clean_value elif clean_value is not None: kwargs[str(opt)] = clean_value elif opt == "type": # we'll allow these through and assume they meant content type from django.contrib.contenttypes.models import ContentType arg = arg.lower().replace('_', '').replace('-', '') app_labels = fetch_metadata_apps() app_labels.append('aristotle_mdr_help') mods = ContentType.objects.filter(app_label__in=app_labels).all() for i in mods: if hasattr(i.model_class(), 'get_verbose_name'): model_short_code = "".join( map( first_letter, i.model_class()._meta.verbose_name.split(" ") ) ).lower() if arg == model_short_code: token_models.append(i.model_class()) if arg == i.model: token_models.append(i.model_class()) else: query_text.append(word) self.token_models = token_models self.query_text = " ".join(query_text) self.kwargs = kwargs self.boost_ups = boost_ups return kwargs
def get_queryset(self): app = self.kwargs['app'] if self.kwargs['app'] not in fetch_metadata_apps(): raise Http404 return get_concepts_for_apps([app])
def allowable_search_models(): return fetch_metadata_apps() + [ 'aristotle_mdr_help', 'aristotle_mdr_stewards', ] + getattr(settings, 'ARISTOTLE_SEARCH_EXTRA_MODULES', [])
def get_queryset(self): return ConceptHelp.objects.filter( Q(app_label__in=fetch_metadata_apps()) | Q(app_label__isnull=True))