Beispiel #1
0
def access_fmglobal(FORCE_CREATION=False):  # force_creation is used to avoid using the cached DB
    """ 
	Method for loading the Faceted Manager instance and keeping it in memory.
		-> if not loaded already (at server start time) it's loaded from the DB (using pickle)
			-> if the DB cache is disabled, it loads it in memory using the global variable
				-> mind that this last option is not thread safe, so it shouldn't be used in the production environment!!
				-> if using the DB, first issue: 'python manage.py fb_store_facetmanager'
	 """
    FM_GLOBAL = cache.get("DJ_FM_GLOBAL")

    if FORCE_CREATION or ((not DJF_CACHE) and (not FM_GLOBAL)):
        if not FORCE_CREATION:
            djfacetlog(
                "\n\n***********\nInitializing FM_GLOBAL without the cached DB object\n[For faster performance you can pre-initialize the Faceted Manager instance and store it in the DB\nUse the management command 'djfacet_fmcache', and set DJF_CACHE = True in settings.py]\n***********\n",
                True,
            )
        else:
            djfacetlog("\n\n***********\nInitializing FM_GLOBAL with <FORCE_CREATION=True>\n***********\n", True)
        loaded_facet_groups = []
        facets_for_template = []

        # load facet specs and init the Faceted Manager object
        # 1: create groups from SPECS.facet_groups	//	2: load facets into groups using SPECS.facetslist
        valid_groups = [
            x for x in reversed(sorted(DJF_SPECS.facet_groups, key=lambda (k): k["position"])) if x["default"]
        ]
Beispiel #2
0
def home(request):
    """
    Main dispatcher: checks if it is the first page (no filters and not item) of the FB, and redirects accordingly.

    The DJF_SPLASHPAGE constant defaults to True and indicates that the all_facets splash page is desired.

    The <search_page> view usually returns a (template, context) tuple; however if the query filters are invalid,
    it tries to remove the wrong ones, recompose the url and issue a redirect command.
    In this case a HttpResponseRedirect is returned, not a tuple, so an if statement handles that situation.
    """
    djfacetlog("Home view.")
    query_filtersUrl = request.GET.getlist('filter')
    item = request.GET.get('item', None)
    resulttype = validate_ResType(request.GET.get('resulttype', None))

    if DJF_SPLASHPAGE and not query_filtersUrl and not item:
        # redirect to the all-facets page
        return redirect("allfacets/?resulttype=%s" % resulttype)
    elif item:
        # contains an item, redirect to the single page.
        djfacetlog("single item %s " % item)
        results = single_item(request, item)
    else:
        # it's classic search page
        results = search_page(request)
    # finally..
    if type(results) == type(('a tuple',)):
        template, context = results
        return render_to_response(template,
                                  context,
                                  context_instance=RequestContext(request))
    else:
        return results  # in this case it's a HttpResponseRedirect object
Beispiel #3
0
    def buildfacets_fromspecs(self, facetspecs, REMOVE_EMPTY_VALUES=True):
        """Creates facets objects from a list of dictionaries specifying how the facets are defined, 
			using 'grouping' to find out which are the right facets to explode e.g.:
			[{	'label' : 'surname' , 
				'model' : Person , 
				'grouping' : "group_one", }, .... ]
				
			REMOVE_EMPTY_VALUES: defaults to true, makes sure we move all empty values from facets!	
			If we set it to False, the FB works but there's a bug with the cache... TODO
			
			2012-05-22: added 'active' to facetspecs definition - quick way to deactive a facet - defaults to True. 
			
			
		"""
        for i in facetspecs:
            if self.uniquename in i["appearance"]["grouping"] and i.get("active", True):

                model = i.get("model")  # required
                uniquename = i.get("uniquename")  # required
                mptt = i.get("mptt", False)
                explanation = i.get("explanation", "")
                appearance_specs = i["appearance"]
                mask = appearance_specs.get("mask", None)
                customvalues = appearance_specs.get("customvalues", None)
                dbfield = appearance_specs.get("dbfield", None)
                displayfield = appearance_specs.get("displayfield", None)
                hierarchyoptions = appearance_specs.get("hierarchy", None)  # ... or the hierarchy opts
                show_singlefacet_header = appearance_specs.get("show_singlefacet_header", True)
                number_opts = appearance_specs.get("number_opts", None)
                ordering = appearance_specs.get("ordering", None)
                exclude = appearance_specs.get("exclude", False)

                behaviour = i.get("behaviour", None)  # behavior of each facet//result_type
                group = self
                # Facet(name, originalModel, originalAttribute, displayAttribute = None, behaviour = None, hierarchyoptions= None,  mask=None, customvalues = None, mptt = False, exclude=False, group=[])
                djfacetlog("..adding facet: %s" % uniquename, True)
                x = Facet(
                    uniquename,
                    appearance_specs["label"],
                    model,
                    dbfield,
                    displayfield,
                    ordering,
                    behaviour,
                    hierarchyoptions,
                    number_opts,
                    mask,
                    customvalues,
                    mptt,
                    explanation,
                    group=group,
                    show_singlefacet_header=show_singlefacet_header,
                )

                self.facets.append(x)
                # also instantiates all the values!
                x.create_allfacetvalues()

                if REMOVE_EMPTY_VALUES:  # by default yes
                    x.remove_all_empty_values()
def singlefacet(request, facet_name=None):
    """
    2011-11-02: shows all the values available for a facet, and lets users select one
    """
    FM_GLOBAL = access_fmglobal()
    if facet_name and FM_GLOBAL.get_facet_from_name(facet_name):
        page, resulttype, ordering, query_filtersUrl, query_filtersBuffer, activeIDs, item, totitems, showonly_subs, history = __extractGETparams(
            request)

        redirect_flag, query_filtersUrl_Clean = __validateQueryFilters(resulttype, query_filtersUrl, FM_GLOBAL)
        if redirect_flag:
            djfacetlog("FacetViews>> Redirecting; the url contained  invalid filter values!", True)
            newurl_stub = create_queryUrlStub(query_filtersUrl_Clean)
            newurl_stub = "%s?resulttype=%s%s" % (request.path, resulttype, newurl_stub)
            return redirect(newurl_stub)

        newurl_stub = create_queryUrlStub(query_filtersUrl)
        queryargs = []
        queryargs = [FM_GLOBAL.get_facetvalue_from_id(fvalue_id) for fvalue_id in query_filtersUrl]

        tree = None
        if showonly_subs:
            top_value = FM_GLOBAL.get_facetvalue_from_id(showonly_subs)
            if top_value:
                tree = FM_GLOBAL.get_facet_from_name(facet_name).recursive_tree_forfacetvalue(top_value)


        # print "\nARGs:\n",  showonly_subs, tree, "\n\n"

        djfacetlog(
            "\n\n**** NewDJfacet Query ****\n\n.. action = SINGLE Facet\n... facet = %s \n.... resulttype = %s\n..... showOnlySubs = %s\n" % (
            facet_name, str(resulttype), str(showonly_subs)), True)

        context = {
        'user_is_logged_in': request.user.is_authenticated(),
        'DJF_STATIC_PATH': DJF_STATIC_PATH,
        'djfacet_singlefacet': True,

        'result_types': DJF_SPECS.result_types,
        'result_typeObj': FM_GLOBAL.get_resulttype_from_name(resulttype),
        'newurl_stub': newurl_stub,
        'url_prefix': "../../", # need to go up two levels!
        'facetvalues': FM_GLOBAL.refresh_facetvalues(queryargs=queryargs, activeIDs=activeIDs,
                                                     resulttype_name=resulttype,
                                                     facet=FM_GLOBAL.get_facet_from_name(facet_name),
                                                     showonly_subs=showonly_subs),
        'facet': FM_GLOBAL.get_facet_from_name(facet_name),
        'query_filtersBuffer': queryargs,
        'totitems': totitems,
        'tree': tree
        }

        return render_to_response('djfacet/single_facet.html',
                                  context,
                                  context_instance=RequestContext(request))
    else:
        raise Http404
def __allfacets_view(request, CACHE_ONTHEFLY=False):
    """
    If we get here, it means that the Cache for the splash page hasn't been found

    If the 'CACHE_ONTHEFLY' variable is set to true, we store the cached-page after it's been calculated

    """
    FM_GLOBAL = access_fmglobal()
    resulttype = validate_ResType(request.GET.get('resulttype', None))

    # CREATE THE FACETS COLUMNS:
    djfacetlog(
        "\n\n**** NewDJfacet Query ****\n\n.. action = ALL Facets\n... resulttype = %s \n.... DJF_MAXRES_ALLFACETS = %s	  \n" % (
        str(resulttype), str(DJF_MAXRES_ALLFACETS)), True)
    facetgroups_and_facets = []
    for group in FM_GLOBAL.facetsGroups:
        facetgroups_and_facets.append((group, [(facet, FM_GLOBAL.refresh_facetvalues(queryargs=[], activeIDs=[],
                                                                                     resulttype_name=resulttype,
                                                                                     facet=facet,
                                                                                     LIMIT=DJF_MAXRES_ALLFACETS)) for
                                               facet in group.facets if facet.get_behaviour(resulttype)]))

    # RESET THE SESSION INFO
    request.session['active_resulttype'] = resulttype    # a string (=uniquename) ... used by updateFacets
    request.session['query_filtersBuffer'] = [] # a list of FValues..
    request.session['activeIDs'] = []
    # request.session.set_expiry(300) # 5 minutes ..	too much?

    context = {
    'DJF_STATIC_PATH': DJF_STATIC_PATH,
    'user_is_logged_in': request.user.is_authenticated(),
    'facetgroups_and_facets': facetgroups_and_facets,

    'ajaxflag': False, # trick so that in this template the contents of facets are shown
    'twocolumnsflag': True,
    'djfacet_splashpage': True,

    'result_types': DJF_SPECS.result_types,
    'result_typeObj': FM_GLOBAL.get_resulttype_from_name(resulttype),
    'newurl_stub': "", # empty cause there are no filter here
    'url_prefix': "../", # need to go up one level!
    }

    if CACHE_ONTHEFLY:
        # render the template
        template = get_template('djfacet/all_facets.html')
        text = template.render(RequestContext(request, context))
        # cache it
        cacheHTMLpage(text, "all_facets", extraargs=resulttype)
        return text
    else:
        # rendering done somewhere else
        return ('djfacet/all_facets.html', context)
Beispiel #6
0
    def optimize_query(self, queryargs, resulttype_name):
        """ Changes the order of queryargs for better performance [2010-11-03]

			At the moment it works only if DB-CACHING is ON: it reorders the args depending on the number of 
			results they might produce (these are stored in CachedFacetValue table as 'count'). 
			The queryarg producing less results is applied first to the queryset filtering routine. 
			
			TODO: when there's no cache (=actively update db-FB) ... we should probably first launch the queries that have
			less joins (so to reduce the number of results), then the others..
		 """

        if DJF_CACHE:  # TODO FIX QUERYARGS
            djfacetlog("RUN_QUERY: optimizing queryargs order through cached values....")
            cacheDB = DbCache(self, queryargs, None)
            queryargs = cacheDB.updateQueryargsOrder_bycount(resulttype_name)
            cacheDB = None
        return queryargs
Beispiel #7
0
    def init_resulttypes_activeIDs(self, resulttype_name=None):
        """ This is not called at loading time, but only when necessary....
			- if a specific result type is passed, we calculate the list of all IDs and return it
			- if no arg is passed, we just calculate the TOT active IDs for all res types 
		"""
        if resulttype_name:
            res_type = self.get_resulttype_from_name(resulttype_name)
            queryset = self.all_results(resulttype_name)
            if queryset and not res_type["all_IDs"]:
                res_type["all_IDs"] = [x.id for x in queryset]
                djfacetlog("INITIALIZED tot active IDs for -- %s --" % resulttype_name)
                return res_type["all_IDs"]
        else:
            for res_type in self.result_types:
                queryset = self.all_results(res_type["uniquename"])
                if queryset and not res_type["all_IDs"]:
                    djfacetlog("INITIALIZED tot active IDs for -- %s --" % res_type["uniquename"])
                    res_type["all_IDs"] = [x.id for x in queryset]
def __update_results(resulttype, ordering, query_filtersUrl, query_filtersBuffer, activeIDs):
    """
    Method that runs a query via the faceted browser.

    The new approach is that each query contains in the GET (query_filtersUrl) all the
    necessary information for constructing queryargs.
    The Buffer is used only to determine the 'action', when possible, and adopt alternative heuristics for faster performance

    RESULTTYPE: string representing what result-types are selected (e.g., 'factoids')
    ORDERING: string used to pass ordering directives (starts with '-' for descending order)

    """

    FM_GLOBAL = access_fmglobal()
    items = None

    # remember that query_filtersBuffer is a list of FV objects, so the ID is a number, not a string!
    query_filtersBufferIDs = [force_unicode(x.id) for x in query_filtersBuffer if x]

    filters_difference = list_difference(query_filtersUrl, query_filtersBufferIDs)

    if len(query_filtersUrl) == 0:
        action = "all"
    elif len(filters_difference) == 1:
        if (len(query_filtersUrl) - len(query_filtersBufferIDs)) == 1:
            action = "add"
            #make sure the latest element is last! (this is needed by the FM run_query algorithm)
            query_filtersUrl.remove(filters_difference[0])
            query_filtersUrl.append(filters_difference[0])
        if (len(query_filtersBufferIDs) - len(query_filtersUrl)) == 1:
            action = "remove"
    else:
        # in all the other cases..we can't establish a continuity with the previous query (difference = 2 or more..) ... thus just apply the filters sequentially
        action = "reload"

    # No need to check for valid Ids here: it's already been done in the 'Home' function
    #  TIP: we must set it to null first, in order to avoid caching!!!!
    queryargs = []
    queryargs = [FM_GLOBAL.get_facetvalue_from_id(fvalue_id) for fvalue_id in query_filtersUrl]

    djfacetlog(
        "\n\n**** NewDJfacet Query ****\n\n.. action = %s\n... query_filtersUrl = %s \n.... query_filtersBuffer_IDs = %s	  \n..... **filters_difference** = %s\n...... queryargs = %s\n" % (
        action, str(query_filtersUrl), str(query_filtersBufferIDs), str(filters_difference),
        str([q.id for q in queryargs])), True)
    djfacetlog("\n....ordering is %s\n" % str(ordering))

    # RUN THE QUERY
    result = FM_GLOBAL.run_query(queryargs, resulttype, activeIDs, ordering, action)
    new_activeIDs = result[0]
    items_list = result[1]

    djfacetlog("+++++++++++ FACETVIEW: new_activeIDs: [%d] ... now starting Pagination ....." % len(new_activeIDs))

    return [items_list, queryargs, new_activeIDs]
Beispiel #9
0
 def init_resulttypes_count(self):
     """init the TOTAL NUMBER of result_type allIds from the name """
     for i in self.result_types:
         if DJF_CACHE:
             cachedResult = CachedFacetQuery.objects.filter(facet="None", resulttype=i["uniquename"])
             if cachedResult:
                 i["total_count"] = cachedResult[0].tot_ids
                 djfacetlog("INITIALIZED total count for -- %s -- from Database CACHE" % i["uniquename"])
             else:
                 queryset = self.all_results(i["uniquename"])
                 if queryset:
                     i["total_count"] = queryset.count()
                     djfacetlog("Tried to initialize from database Cache .. FAILED for -- %s --" % i["uniquename"])
                     djfacetlog("INITIALIZED total count without Cache for -- %s --" % i["uniquename"])
         else:
             queryset = self.all_results(i["uniquename"])
             if queryset:
                 i["total_count"] = queryset.count()
                 djfacetlog("INITIALIZED total count for -- %s --" % i["uniquename"])
def __validateQueryFilters(resulttype, query_filtersUrl, FM_GLOBAL):
    """
    Check That the Ids Passed in Filters_Url Are Existing and Valid for This Result Type
    Othersise return a flag, and a 'purified' version of the query filters, so that we can redirect the request to a valid URL
    -- This Way We Always Maintain a One-To-One Correspondance Between Url Filters and Args in Query

    MPTT values: this is a special case; if we find an MPTT value we check that there aren't any 'children' also selected as filters.
        If there are children, the father is removed cause the query is more specific.
    """
    redirect_flag, query_filtersUrl_Clean = False, []
    for fvalue_id in query_filtersUrl:
        try:
            facetvalue = FM_GLOBAL.get_facetvalue_from_id(fvalue_id)
            if facetvalue.facet.get_behaviour(resulttype) and not facetvalue.facet.mptt:
                query_filtersUrl_Clean.append(fvalue_id)
            elif facetvalue.facet.get_behaviour(resulttype):
                if facetvalue.facet.mptt:
                    test_mpttfather = False
                    all_vals = [FM_GLOBAL.get_facetvalue_from_id(x) for x in query_filtersUrl]
                    for v in all_vals:
                        if v.father and (v.father == facetvalue.id):
                            test_mpttfather = True
                    if test_mpttfather:
                        redirect_flag = True
                        djfacetlog(
                            "FacetViews>> The requested facetvalue [%s] already has an MPTT children selected, thus it will be removed (resulttype [%s])" % (
                            fvalue_id, resulttype), True)

                    else:
                        query_filtersUrl_Clean.append(fvalue_id)

            else:
                redirect_flag = True
                djfacetlog("FacetViews>> The requested facetvalue [%s] doesn't have a behaviour for resulttype [%s]" % (
                fvalue_id, resulttype), True)
        except Exception, e:
            redirect_flag = True
            djfacetlog("FacetViews>> Can't identify facetvalue from ID! Error: %s" % (e), True)
            continue
Beispiel #11
0
            x = valid_groups[n]
            bkcolor = x.get("bkcolor", getHTMLColor(n))
            loaded_facet_groups.append(FacetsGroup(x["uniquename"], x["label"], x["position"], bkcolor=bkcolor))
        for g in loaded_facet_groups:
            g.buildfacets_fromspecs(DJF_SPECS.facetslist)

        RESULT_TYPES = DJF_SPECS.result_types

        # initialize the faceted manager and add it to the django cache

        FM_GLOBAL = FacetedManager(loaded_facet_groups, RESULT_TYPES)
        cache.set("DJ_FM_GLOBAL", FM_GLOBAL, 600)  # 10 minutes

    if DJF_CACHE and not FM_GLOBAL and not FORCE_CREATION:
        djfacetlog(
            "\n\n***********\nInitializing FM_GLOBAL: using database-cached version and LOCK mechanism to make it thread-safe\n***********\n",
            True,
        )
        from django.db import connection

        cursor = connection.cursor()
        try:
            cursor.execute("LOCK TABLES %s WRITE" % "djfacet_cachedfacetedmanager")
            x = CachedFacetedManager.objects.all()[0]
            FM_GLOBAL = x.manager
        except:
            raise Exception(
                "\n***** DJFACET : could not init the Faceted Manager object from Database: have you created it? Use the 'djfacet_fmcache' management command to create it, or set DJF_CACHE to False"
            )
        finally:
            cursor.execute("UNLOCK TABLES")
            cursor.close()
Beispiel #12
0
    def refresh_facetvalues(self, queryargs, activeIDs, resulttype_name, facet, LIMIT=None, showonly_subs=False):
        """
		************
		Method called to instantiate the QueryHelper and refresh the FacetValues count for the active query.
		
		<activeIDs>: precalculated list of object IDs, essential for running this method. 
					If not available (eg because the session has expired, or because a search page is loaded directly via a url) 
					needs to be recalculated via self.run_query
		<showonly_subs>: a flag indicating that we're only querying for the direct subs of a specific value
		*********
		"""

        # 1. test the DB cache
        cache_test = None
        if DJF_CACHE:
            djfacetlog("+++> DB-CACHE: .. trying to get values from DB for __%s__ ..." % facet.name)
            cacheDB = DbCache(self, queryargs, activeIDs)
            cache_test = cacheDB.getCachedFacetValues(resulttype_name, facet, LIMIT, showonly_subs)
            if cache_test:
                djfacetlog("	   -----> SUCCESS: Retrieved values.....")
                return cache_test
            else:
                cache_test = None
                djfacetlog("	   -----> FAILED: Could not retrieve any value from DB cache...")

                # 2. calculate the fv counts ==> cause the DBcache is switched off, or there's no cached data available
        if cache_test == None:
            # if there are no facets selected, make sure we get the correct activeIDs number
            if not queryargs:
                saved_activeIDs = self.get_resulttype_allIDs(resulttype_name)
                if saved_activeIDs == None:
                    # .. if we still haven't cached all activeIDs, run 'init'
                    activeIDs = self.init_resulttypes_activeIDs(resulttype_name)
                else:
                    activeIDs = saved_activeIDs

                q = QueryHelper(resulttype_name, facet, activeIDs, queryargs, limit=LIMIT, showonly_subs=showonly_subs)

            else:
                if not activeIDs:
                    # If the activeIDs got lost for some reason RUN THE QUERY AGAIN
                    # (the activeIDs are stored in the session for 5mins only.This makes it possible to open up a single_facet page from scratch too)
                    djfacetlog("\n+++> REFRESH_FACETVALUES: ActiveIDs not available.... recalculating them........")
                    activeIDs = self.run_query(queryargs, resulttype_name, activeIDs, action="reload")[0]
                q = QueryHelper(resulttype_name, facet, activeIDs, queryargs, limit=LIMIT, showonly_subs=showonly_subs)

            djfacetlog(
                "\n+++> REFRESH_FACETVALUES:  calculating values using no cache for facet	 __%s__	 and LIMIT=%s"
                % (facet.name, str(LIMIT))
            )
            # calc the facetvalues using the QueryHelper obj...
            valueslist = q.calculate_facetvalues()

            # CACHE ON THE FLY, IF IT'S A FULL QUERY ONLY
            if DJF_CACHE and not LIMIT and not showonly_subs:
                if not queryargs:
                    result_type = self.get_resulttype_from_name(resulttype_name)
                    if cacheDB._cacheOneFacet(facet, result_type, values_to_cache=valueslist):
                        djfacetlog("		  ...... on-the-fly DB-Cache operation successfull...!")
                else:
                    pass  # we're DB-caching on the fly only when there are no queryargs !!!!!!!!

                    # finally......
            return valueslist
def search_page(request):
    """
    Function that returns a tuple (template, context).

    Normally it's used to calculate a query from one or more filters.
    (if DJF_SPLASHPAGE_CACHE is set to False, it returns all values and all facets)

    """
    FM_GLOBAL = access_fmglobal()
    page, resulttype, ordering, query_filtersUrl, query_filtersBuffer, activeIDs, item, totitems, showonly_subs, history = __extractGETparams(
        request)
    table_template = None

    # CHECK THAT THE IDS PASSED IN FILTERS_URL ARE EXISTING AND VALID FOR THIS RESULT TYPE, otherwise redirect
    redirect_flag, query_filtersUrl_Clean = __validateQueryFilters(resulttype, query_filtersUrl, FM_GLOBAL)
    if redirect_flag:
        djfacetlog("FacetViews>> Redirecting; the url contained  invalid filter values!", True)
        newurl_stub = create_queryUrlStub(query_filtersUrl_Clean)
        newurl_stub = "%s?resulttype=%s%s" % (request.path, resulttype, newurl_stub)
        return redirect(newurl_stub)

    # ORDERINGS: if not provided it defaults to the standard one (as defined in the model)
    try:
        from djfacet.orderings import determine_ordering

        real_ordering = determine_ordering(resulttype, ordering)
    except:
        real_ordering = 'default'

    # =========
    # UPDATE_RESULTS (note: new_queryargs has the new format=list of fvalues!)
    new_items, new_queryargs, new_activeIDs = __update_results(resulttype, real_ordering, query_filtersUrl,
                                                               query_filtersBuffer, activeIDs)
    # =========


    # PAGINATION
    # ===>> rem that: 'items' is not a collection, so they are accessed through 'objects_list' method in the template
    paginator = Paginator(new_items, DJF_MAXRES_PAGE) # ie show 50 items per page
    try:
        items = paginator.page(page)
    except (EmptyPage, InvalidPage):  # If page request is out of range, deliver last page of results.
        items = paginator.page(paginator.num_pages)
    # add other useful paginator data to the object
    items.extrastuff = paginator_helper(page, paginator.num_pages)
    items.totcount = paginator.count

    # (RE)CREATE THE FACETS COLUMN:  new 2012-01-12 removed the <get_facetsForTemplate> routine
    facetgroups_and_facets = []
    if DJF_AJAX:
        for group in FM_GLOBAL.facetsGroups:
        # in this case we don't need to preload all facet contents, cause they'll be updated via ajax
            facetgroups_and_facets.append(
                (group, [(facet, []) for facet in group.facets if facet.get_behaviour(resulttype)]))
    else:
        # set LIMIT to DJF_MAXRES_FACET/None to switch
        for group in FM_GLOBAL.facetsGroups:
            facetgroups_and_facets.append((group,
                                           [(facet, FM_GLOBAL.refresh_facetvalues(queryargs=new_queryargs,
                                                                                  activeIDs=new_activeIDs,
                                                                                  resulttype_name=resulttype,
                                                                                  facet=facet, LIMIT=DJF_MAXRES_FACET))
                                            for
                                            facet in group.facets if facet.get_behaviour(resulttype)]))

    # HELPER URL STRINGA creation, from the active filters
    newurl_stub = create_queryUrlStub(query_filtersUrl)

    # RESET THE SESSION INFO
    request.session['query_filtersBuffer'] = new_queryargs    # a list of FValues..
    request.session['activeIDs'] = new_activeIDs
    request.session['active_resulttype'] = resulttype    # a string (=uniquename) ... used by updateFacets
    # print history
    history = update_history("search", history, new_queryargs, FM_GLOBAL.get_resulttype_from_name(resulttype),
                             newurl_stub)
    request.session['djfacet_history'] = history
    # print history
    # request.session.set_expiry(300) # 5 minutes ..	too much?


    # GET THE RIGHT TEMPLATE FOR RESULTS LIST
    table_template = select_template(["djfacet/%s/%s_table.html" % (resulttype, resulttype),
                                      "djfacet/results/generic_table.html"])
    table_template = table_template.name

    context = {
    'user_is_logged_in': request.user.is_authenticated(),
    'facetgroups_and_facets': facetgroups_and_facets,
    'items': items,
    'totitems': items.totcount,

    'DJF_STATIC_PATH': DJF_STATIC_PATH,
    'ajaxflag': DJF_AJAX,
    'twocolumnsflag': False,
    'djfacet_searchpage': True,

    'result_types': DJF_SPECS.result_types,
    'result_typeObj': FM_GLOBAL.get_resulttype_from_name(resulttype),
    'ordering': ordering,

    'query_filtersBuffer': new_queryargs,
    'newurl_stub': newurl_stub,
    'table_template': table_template,

    'recent_actions': history

    }

    return ('djfacet/facetedbrowser.html', context)
def search(request):
    """
    Handler for basic query search bar.
    """
    djfacetlog("Search Bar")
    print "Runs here"
Beispiel #15
0
    def run_query(self, queryargs, resulttype_name, activeIDs, ordering="default", action="default"):
        """		
		Method for running a query on the information space (which is dependent on the 
			resulttype value passed). 
						
		queryargs = a list of facetvalues 
		resulttype_name = string representing what result-types are selected (e.g., 'people')
		activeIDs = list of the objects-IDs in current result set
		action = the type of filtering action to be done (eg zomming in/out)
		
		TODO: 
		<explain algorithm>
				
		"""

        infospace = self.all_results(resulttype_name)  # returns the top-level queryset
        if not infospace:
            raise Exception, "RUN_QUERY: Error: result type not found - could not continue"

        result = infospace

        if action == "all":
            djfacetlog("RUN_QUERY: *all* action......")
            djfacetlog("ActiveIDs: %d [the entire result set!]" % self.get_resulttype_count(resulttype_name))
            djfacetlog("====now setting ActiveIDs to zero....")
            activeIDs = []

        if action == "add":
            # Adding 1 new filter: we try to speed up the query by re-using the previously stored active-IDs
            # NOTE: this approach will fail if we remove the activeIDs from the session object!
            djfacetlog("RUN_QUERY: *add* action... building queryset object using [%s] args.... " % len(queryargs))
            if len(queryargs) > 1:
                djfacetlog(
                    "RUN_QUERY: args>1 & action=add ==>	I'm using the *most recent queryarg* and the *cached activeIds* to speed up the query"
                )
                result = result.filter(id__in=activeIDs)

            result = self._inner_runquery(
                result, queryargs[-1], resulttype_name
            )  # even in case when 1 qarg only is present, [-1] gets the right one
            djfacetlog("====now updating ActiveIDs ....")
            activeIDs = [x.id for x in result]
            djfacetlog("ActiveIDs: %d" % len(activeIDs))

        if action == "remove" or action == "reload":
            djfacetlog(
                "RUN_QUERY: *%s* action... building queryset object using [%s] args.... " % (action, len(queryargs))
            )

            if len(queryargs) > 1:
                queryargs = self.optimize_query(queryargs, resulttype_name)
                for i in range(len(queryargs)):
                    if i == 0:
                        djfacetlog("___calculating queryArg[%d]...." % (i + 1))
                        result = self._inner_runquery(result, queryargs[i], resulttype_name)

                    else:
                        djfacetlog("___calculating TEMP IDs for queryArg[%d]...." % (i + 1))
                        temp_IDs = [x.id for x in result]
                        result = infospace.filter(id__in=temp_IDs)
                        result = self._inner_runquery(result, queryargs[i], resulttype_name)

                        if (i + 1) == (len(queryargs)):
                            djfacetlog("====now updating ActiveIDs ....")
                            activeIDs = [x.id for x in result]
                            djfacetlog("ActiveIDs: %d" % len(activeIDs))

            elif len(queryargs) == 1:
                result = self._inner_runquery(result, queryargs[0], resulttype_name)
                djfacetlog("====now updating ActiveIDs ....")
                activeIDs = [x.id for x in result]
                djfacetlog("ActiveIDs: %d" % len(activeIDs))

            else:  # if we have no queryargs
                djfacetlog("ActiveIDs: %d [the entire result set!]" % self.get_resulttype_count(resulttype_name))
                djfacetlog("====now setting ActiveIDs to zero....")
                activeIDs = []

                # finally, let's do the ordering
                # The 'annotate' syntax was LEFT for BACKWARD COMPATIBILITY, NOW ORDERING IS BETTER EXPRESSED AS A LIST OF STRINGS
        if type(ordering) == type("a string") or type(ordering) == type(u"a unicode string"):
            if ordering == "default":
                return (activeIDs, result)
            else:
                if ordering.split("=")[0] == "annotate":
                    val = ordering.split("=")[1]
                    result = result.annotate(Count(val)).order_by(val + "__count")
                    return (activeIDs, result)
                elif ordering.split("=")[0] == "-annotate":
                    val = ordering.split("=")[1]
                    result = result.annotate(Count(val)).order_by("-" + val + "__count")
                    return (activeIDs, result)
                elif ordering.find(",") > 0:
                    multiplevals = [x.strip() for x in ordering.split(",")]
                    result = result.order_by(*multiplevals)
                    return (activeIDs, result)
                else:
                    result = result.order_by(ordering)

        else:  # ordering is a list of strings....
            result = result.order_by(*ordering)
            return (activeIDs, result)
Beispiel #16
0
    def _inner_runquery(self, queryset, queryarg, resulttype_name):
        """	  
		queryset = a django queryset (e.g. the infospace, the result of a previous query)
		queryarg: a single facet value
		resulttype_name

		ALGORITHM: 
		
		The standard way to costruct dymanic queries with Django is by using **kwargs and exploding it in the query. 
		However this generates a problem when you want to add together two constraints on the same attribute, 
		for in such situation the query is interpreted differently.
			See also: http://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships 
		
		So the chain of 'filters' solution has been adopted, e.g.: 		

		Person.objects.filter(genderkey__fullname='Female', genderkey__fullname='Male')
		==> returns only the Males!!!

		Person.objects.filter(genderkey__fullname='Female').filter(genderkey__fullname='Male')
		==> this is what we want!!! (persons that are both male and female = None)
		
		
		In the case of MPTT filters (=hierarchical facets), if the relationships haven't been manually exploded 
		(i.e. if DJF_MPTT_INHERITANCE is True) we must look for matching results across the whole descendants in a tree.
		This is done by extracting a list of IDs of the descendants for a facetvalues, and passing those in the query.
		E.g. kwargs[linkAttr	 +	"id__in"] = descendants

		"""

        args, kwargs, new_queryset = [], {}, queryset
        linkAttr = ""  # the behaviour specified in the specs = a string or tuple for complex ORs
        rangeAttr = ""  # the range specs
        facet, facetvalue = queryarg.facet, queryarg

        if not facet.get_behaviour(resulttype_name) == None:  # blank is valid behaviour!!!

            if facetvalue.customvalues:
                # CASE 1: we are dealing with custom facets:
                # ******
                # e.g. {'hammondnumber' : 1, 'hammondnumb2__gt' : 0, 'hammondnumb2__lt' :10}
                # mind 1) with custom values, in the 'behavior' you must omit the last bit, ie the 'dbfield'
                # mind 2) that in queries with multiple joins there are two options to build the filters
                # 1) sequencing single .filter(...) arguments and 2) one single filter with all args inside
                # here we were forced to use the second one, as the 1st one would break with many joins (!)
                # it's an issue that requires further investigation....
                # http://docs.djangoproject.com/en/dev/topics/db/queries/#spanning-multi-valued-relationships
                linkAttr = facet.get_behaviour(resulttype_name)  # .behaviour[resulttype_name][0]
                if linkAttr:
                    linkAttr = linkAttr + "__"
                for d in facetvalue.customvalues:
                    # temp = {}
                    kwargs[linkAttr + d] = facetvalue.customvalues[d]
                    djfacetlog("==queryArg found... with CustomValues: [%s]" % (kwargs))
                new_queryset = new_queryset.filter(**kwargs).distinct()
                return new_queryset

            else:
                # CASE 2: we are using Django-based facets (= models):
                # ******
                valore = facetvalue.name
                linkAttr = facet.get_behaviour(resulttype_name)  # .behaviour[resulttype_name][0]

                # SUBcase 1: if we have a behaviour encoded in a single 'string'
                # ----------
                if type(linkAttr) == type(""):
                    # ++++++++++++++++
                    # 1.1 MPTT facets
                    # ++++++++++++++++
                    if facet.mptt and DJF_MPTT_INHERITANCE:

                        mpttinstance = facet.originalModel.objects.get(pk=facetvalue.mpttoriginalID)

                        descendants = [x.id for x in mpttinstance.get_descendants(True)]  # includes this element too
                        if facet.originalAttribute:
                            # since we're completely replacing the original attribute of a query with the MPTT tree information,
                            # we need to remove that querypath element from the querypath defined in facetspecs.
                            # Eg: from <religions__name> to <religions__>
                            linkAttr = linkAttr.rstrip(facet.originalAttribute)

                        kwargs[linkAttr + "id__in"] = descendants
                        djfacetlog("==queryArg found... query with MPTT behaviour: [%s]" % (kwargs,))
                        new_queryset = new_queryset.filter(**kwargs).distinct()

                    else:
                        # ++++++++++++++++
                        # 1.2 Normal facets
                        # ++++++++++++++++
                        if facetvalue.hierarchytype == "alpha":
                            rangeAttr = "__istartswith"
                        if facetvalue.hierarchytype == "range":
                            rangeAttr = "__range"
                            valore = facetvalue.hierarchyextra  # a tuple (100, 2000)
                            # 	finally:
                        kwargs[linkAttr + rangeAttr] = valore  # a dict
                        djfacetlog("==queryArg found... with basic behaviour: [%s]" % (kwargs))
                        new_queryset = new_queryset.filter(**kwargs).distinct()

                    return new_queryset

                    # SUBcase 2: if we have a behaviour encoded in a tuple=	 OR queries
                elif type(linkAttr) == type(("a tuple",)):
                    for el in linkAttr:
                        temp = {}
                        # added the range behaviour also here, but haven't tested yet!
                        if facetvalue.hierarchytype == "alpha":
                            rangeAttr = "__istartswith"
                        if facetvalue.hierarchytype == "range":
                            rangeAttr = "__range"
                            valore = facetvalue.hierarchyextra
                        temp[el + rangeAttr] = valore
                        djfacetlog("==queryArg found... with OR behaviour: [%s]" % (temp))
                        args.append(Q(**temp))

                    or_args = reduce(operator.or_, args)  # make it a sequence of ORs
                    new_queryset = new_queryset.filter(or_args, **kwargs).distinct()
                    return new_queryset
def update_facet(request):
    """
    Used in AJAX views to update the contents of a single facet
    """

    FM_GLOBAL = access_fmglobal()
    page, resulttype, ordering, query_filtersUrl, query_filtersBuffer, activeIDs, item, totitems, showonly_subs, history = __extractGETparams(
        request)

    facet_name = request.GET.get('activefacet', None)
    facet = FM_GLOBAL.get_facet_from_name(facet_name)

    active_filters = request.GET.getlist('active_filters')    # values hard-coded in the html page!
    # print "\n\n++active_filters=	 " + str(active_filters)

    if not (facet and resulttype):
        return HttpResponse("An error occured: please reload the page. (resulttype=[%s] facet=[%s])" % (
        str(resulttype), str(facet_name)))


    # CHECK THAT THE IDS PASSED IN FILTERS_URL ARE EXISTING AND VALID FOR THIS RESULT TYPE, otherwise redirect
    redirect_flag, query_filtersUrl_Clean = __validateQueryFilters(resulttype, query_filtersUrl, FM_GLOBAL)
    if redirect_flag:
        djfacetlog("FacetViews>> Redirecting; the url contained  invalid filter values!", True)
        newurl_stub = create_queryUrlStub(query_filtersUrl_Clean)
        newurl_stub = "%s?resulttype=%s%s" % (request.path, resulttype, newurl_stub)
        return redirect(newurl_stub)

    # CHECK IF WE CAN USE THE SESSION DATA: if not, the back button has been utilized!
    if not len(query_filtersUrl) == len(query_filtersBuffer):
        activeIDs = []
        queryargs = [FM_GLOBAL.get_facetvalue_from_id(fvalue_id) for fvalue_id in query_filtersUrl]
        # RESET THE SESSION INFO
        request.session['query_filtersBuffer'] = queryargs    # a list of FValues..
        request.session['activeIDs'] = activeIDs
    else:
        queryargs = query_filtersBuffer
    #
    # try:
    # 	totitems = int(totitems)
    # except:
    # 	totitems = 0


    # TRY THIS TO DEBUG: WHEN YOU USE THE BACK BUTTON, IN CHROME, THE ACTIVEIDS ARE SET BACK TO 0!
    # WHILE IN SAFARI THIS NEVER HAPPENS...
    # print "\nqueryargs=%s\nactiveIDs=%s\nresulttype=%s\nfacet=%s" % (str(queryargs), str(len(activeIDs)), str(resulttype), str(facet))
    newvalues = FM_GLOBAL.refresh_facetvalues(queryargs, activeIDs, resulttype, facet, LIMIT=DJF_MAXRES_FACET)
    # print "\n\n ____	" + str(newvalues)

    # HELPER URL STRINGA creation, from the active filters
    query_filtersBufferIDs = [force_unicode(x.id) for x in queryargs]
    newurl_stub = create_queryUrlStub(query_filtersBufferIDs)

    innerfacet_template = 'djfacet/components/snippet_facet.html'

    context = Context({'facetvalues': newvalues,
                       'facetgroup': facet.group,
                       'facet': facet,
                       'newurl_stub': newurl_stub,
                       'result_typeObj': FM_GLOBAL.get_resulttype_from_name(resulttype),
                       'totitems': totitems or len(activeIDs), #when using dbcache, totitems may fails
                       'ordering': ordering,
                       'DJF_STATIC_PATH': DJF_STATIC_PATH,
    })

    return_str = render_block_to_string(innerfacet_template,
                                        'inner_facet_values',
                                        context)

    return HttpResponse(return_str)
def single_item(request, item):
    """
    """
    FM_GLOBAL = access_fmglobal()
    page, resulttype, ordering, query_filtersUrl, query_filtersBuffer, activeIDs, item, totitems, showonly_subs, history = __extractGETparams(
        request)
    single_item_template, table_template = None, None
    redirect_flag, query_filtersUrl_Clean = __validateQueryFilters(resulttype, query_filtersUrl, FM_GLOBAL)
    if redirect_flag:
        djfacetlog("FacetViews>> Redirecting; the url contained  invalid filter values!", True)
        newurl_stub = create_queryUrlStub(query_filtersUrl_Clean)
        newurl_stub = "%s?resulttype=%s%s" % (request.path, resulttype, newurl_stub)
        return redirect(newurl_stub)

    # HELPER URL STRINGA creation, from the active filters
    newurl_stub = create_queryUrlStub(query_filtersUrl)

    # CHECK THAT ITEM EXISTS
    try:
        item_obj = FM_GLOBAL.get_resulttype_from_name(resulttype)['infospace'].objects.get(pk=item)
    except:
        newurl_stub = "%s?resulttype=%s%s" % (request.path, resulttype, newurl_stub)
        return redirect(newurl_stub)

    if getattr(item_obj, "get_absolute_url", None):
        item_obj.fullrecord = True
    try:
        item_obj.next = activeIDs[activeIDs.index(item_obj.id) + 1]
    except:
        pass
    try:
        i = activeIDs.index(item_obj.id)
        if i > 0:
            item_obj.prev = activeIDs[i - 1]
    except:
        pass

    # GET THE RIGHT TEMPLATE FOR RESULTS LIST
    single_item_template = select_template(["djfacet/%s/%s_item.html" % (resulttype, resulttype),
                                            "djfacet/results/generic_item.html"])
    single_item_template = single_item_template.name

    if single_item_template == "djfacet/results/generic_item.html":
        # IF THE SINGLE_ITEM TEMPLATE IS THE DEFAULT ONE, JUST OUTPUT ALL FIELDS NAMES..
        this_model = FM_GLOBAL.get_resulttype_from_name(resulttype)['infospace']
        item_obj.fields = __getInstance_PreviewDict(this_model, item_obj)


    # UPDATE HISTORY
    history = update_history('single_item', history, item_obj, FM_GLOBAL.get_resulttype_from_name(resulttype),
                             newurl_stub)
    request.session['djfacet_history'] = history

    context = {
    'user_is_logged_in': request.user.is_authenticated(),

    'DJF_STATIC_PATH': DJF_STATIC_PATH,
    'ajaxflag': DJF_AJAX,
    'twocolumnsflag': False,

    'result_types': DJF_SPECS.result_types,
    'result_typeObj': FM_GLOBAL.get_resulttype_from_name(resulttype),
    'ordering': ordering,

    'query_filtersBuffer': query_filtersBuffer,
    'newurl_stub': newurl_stub,

    'single_item': item_obj,
    'single_item_template': single_item_template,

    'recent_actions': history

    }

    return ('djfacet/facetedbrowser.html', context)
Beispiel #19
0
    def buildfacets_fromspecs(self, facetspecs, REMOVE_EMPTY_VALUES=True):
        """Creates facets objects from a list of dictionaries specifying how the facets are defined, 
			using 'grouping' to find out which are the right facets to explode e.g.:
			[{	'label' : 'surname' , 
				'model' : Person , 
				'grouping' : "group_one", }, .... ]
				
			REMOVE_EMPTY_VALUES: defaults to true, makes sure we move all empty values from facets!	
			If we set it to False, the FB works but there's a bug with the cache... TODO
			
			2012-05-22: added 'active' to facetspecs definition - quick way to deactive a facet - defaults to True. 
			
			
		"""
        for i in facetspecs:
            if self.uniquename in i['appearance']['grouping'] and i.get(
                    "active", True):

                model = i.get('model')  # required
                uniquename = i.get('uniquename')  # required
                mptt = i.get('mptt', False)
                explanation = i.get('explanation', "")
                appearance_specs = i['appearance']
                mask = appearance_specs.get('mask', None)
                customvalues = appearance_specs.get('customvalues', None)
                dbfield = appearance_specs.get('dbfield', None)
                displayfield = appearance_specs.get('displayfield', None)
                hierarchyoptions = appearance_specs.get(
                    'hierarchy', None)  # ... or the hierarchy opts
                show_singlefacet_header = appearance_specs.get(
                    'show_singlefacet_header', True)
                number_opts = appearance_specs.get('number_opts', None)
                ordering = appearance_specs.get('ordering', None)
                exclude = appearance_specs.get('exclude', False)

                behaviour = i.get('behaviour',
                                  None)  # behavior of each facet//result_type
                group = self
                # Facet(name, originalModel, originalAttribute, displayAttribute = None, behaviour = None, hierarchyoptions= None,  mask=None, customvalues = None, mptt = False, exclude=False, group=[])
                djfacetlog("..adding facet: %s" % uniquename, True)
                x = Facet(uniquename,
                          appearance_specs['label'],
                          model,
                          dbfield,
                          displayfield,
                          ordering,
                          behaviour,
                          hierarchyoptions,
                          number_opts,
                          mask,
                          customvalues,
                          mptt,
                          explanation,
                          group=group,
                          show_singlefacet_header=show_singlefacet_header)

                self.facets.append(x)
                # also instantiates all the values!
                x.create_allfacetvalues()

                if REMOVE_EMPTY_VALUES:  # by default yes
                    x.remove_all_empty_values()