def test_split_tags(self): CHECK = lambda x, y, z = "": \ self.assertEqual(sorted(split_tags(x)), sorted(y), z) CHECK("first, second, third", ["first", "second", "third"]) CHECK("First, seCOND, THIRD", ["First", "seCOND", "THIRD"], "should keep cases") CHECK("two words", ["two words"], "should support multiple word tags") CHECK("two words", ["two words"], "should remove extra spaces") CHECK("one, one two, one two three", ["one", "one two", "one two three"]) CHECK("a,b,c,b", ["a", "b", "c"], "should remove duplicated") CHECK("b,,a,,,,c,,d", ["a", "b", "c", "d"], "should remove empty tags") CHECK(["already", "split"], ["already", "split"]) CHECK(None, [], "should accept None as empty list") with self.assertRaises(ValueError): split_tags({"a": 124}) with self.assertRaises(ValueError): split_tags(12312313)
def view(request): q = request.GET.get('q') if q is not None: q = split_tags(q) error = [] tags = list(get_available_tags(q or []).values_list('name', flat=True)) if q is not None: if not q: error.append('Navedite barem jednu oznaku!') elif len(tags) != len(q): diff = set([x.lower() for x in q]) - set([x.lower() for x in tags]) error.append(u'Nepostojeć%s: %s!' % ( u'a oznaka' if len(diff) == 1 else 'e oznake', u', '.join(diff), )) kwargs = dict( show_hidden = 'show_hidden' in request.GET, quality_min = request.GET.get('quality_min'), quality_max = request.GET.get('quality_max'), difficulty_min = request.GET.get('difficulty_min'), difficulty_max = request.GET.get('difficulty_max'), ) groups_error = False if request.user.has_perm('advanced_search'): if request.GET.get('q') is not None: advanced_form = AdvancedSearchForm(request.GET, user=request.user) if advanced_form.is_valid(): kwargs['groups'] = advanced_form.cleaned_data['groups'] else: groups_error = True else: advanced_form = AdvancedSearchForm(user=request.user) else: advanced_form = None if not error: tasks = search_tasks(tags, user=request.user, **kwargs) if hasattr(tasks, 'select_related'): tasks = tasks.select_related('author', 'content') else: tasks = Task.objects.none() return render_to_response('search.html', { 'advanced_form': advanced_form, 'any': bool(request.GET), 'errors': '<br>'.join(error), 'form': SearchForm(request.GET), 'groups_error': groups_error, 'search_solved_count': bool(kwargs.get('groups')), 'tasks': tasks, 'tags': tags, }, context_instance=RequestContext(request))
def tag_list_preview(tags): """Given a list or a comma-separated list of tags, just render them without any logic.""" tags = split_tags(tags) no_plus = u'<a href="/search/?q={}">{}</a>' tags_html = [no_plus.format(tag, tag) for tag in tags] return mark_safe(u'<div class="tag-list preview">{}</div>'.format( u" ".join(tags_html)))
def search(tags=None, tag_ids=None): """ Find all objects whose tags make superset of given tags. If any unknown tag given or none tags given at all, returns None. Otherwise, returns SearchCache object. """ if tags: tags = split_tags(tags) if not tags: return None # if no tag given, don't just return all objects # what if an unknown tag is in the list? tag_ids = list(get_available_tags(tags).values_list('id', flat=True)) if len(tag_ids) != len(tags): return None # unknown tag given elif not tag_ids: return None else: tag_ids = split_tag_ids(tag_ids) # Sort by id before calling. return _search_and_cache(sorted(tag_ids))
def reverse_search(input): """ Find all objects whose tags are a subset of given tags. Returns SearchCache object if any (existing) tag given, otherwise None. Example: reverse_search(['imo', '1997']) --> SearchCache pointing to: --> Folder with filter tag 'imo' --> Folder with filter tag 'imo', '1997' (...) Examples of non matching objects: --> Folder with filter tag 'shortlist', '1997' --> Task with tags 'imo', '1997', 'geo' """ input = split_tags(input) if not input: return None # if no tag given, don't just return all objects tags = get_available_tags(input) if len(tags) != len(input): return None tag_ids = [x.id for x in tags] key = _reverse_search_key(tag_ids) try: return SearchCache.objects.get(key=key) except SearchCache.DoesNotExist: pass # Create cache object. cache = SearchCache(key=key) cache.save() cache.tags.set(*tags) # Do not use set_tags here. # Generate SQL query cache_content_type = ContentType.objects.get_for_model(SearchCache) tag_ids = [x.id for x in tags] query = 'SELECT DISTINCT A.object_id, A.content_type_id, A.tag_id FROM tags_taggeditem A' \ ' INNER JOIN tags_taggeditem B' \ ' ON (A.object_id = B.object_id AND A.content_type_id = B.content_type_id)' \ ' WHERE B.tag_id IN (%s) AND B.content_type_id != %d' \ % (','.join([str(id) for id in tag_ids]), cache_content_type.id) # Manually fetch. cursor = connection.cursor() cursor.execute(query) tagged_items = cursor.fetchall() # Generate and save search result. objects = defaultdict(set) for object_id, content_type_id, tag_id in tagged_items: # Seperate tagged items by objects (get tags for each object) objects[(object_id, content_type_id)].add(tag_id) ids_set = set(tag_ids) # Filter only those objects whose tags are subset of the given set of tags SearchCacheElement.objects.bulk_create( SearchCacheElement(object_id=key[0], content_type_id=key[1], cache=cache) for key, obj_tags in objects.iteritems() if obj_tags.issubset(ids_set) ) return cache