Searching (Filtering) on multi models using drf-haystack searchindex and elasticsearch - django-haystack

I have been trying to implement multiple models using rdf-haystack filters but it does not work. Here are my examples:
I have two models search_indexes.py
class PersonSkillIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
person_name = indexes.CharField(model_attr='person_id')
skill_name = indexes.EdgeNgramField(model_attr='skill_id')
autocomplete = indexes.EdgeNgramField()
#staticmethod
def prepare_autocomplete(obj):
return " ".join((obj.skill_id.name))
def get_model(self):
return PersonSkill
def index_queryset(self, using=None):
return self.get_model().objects.all()
def prepare_person_id(self, obj):
return obj.person_id.name
def prepare_skill_id(self, obj):
return obj.skill_id.name
class PersonPositionIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
person_name = indexes.CharField(model_attr='person_id')
position_name = indexes.EdgeNgramField(model_attr='position_id')
autocomplete = indexes.EdgeNgramField()
#staticmethod
def prepare_autocomplete(obj):
return " ".join(( obj.position_id.title))
def get_model(self):
return PersonPosition
def index_queryset(self, using=None):
return self.get_model().objects.all()
def prepare_person_id(self, obj):
return obj.person_id.name
def prepare_position_id(self, obj):
return obj.position_id.name
I want to search on both PersonSkill and PersonPosition models with the following searchQuerySet:
http://localhost:8000/en/api/profiles/search/?skill_name=python&position_name=programmer
Does Haytack filters support this functionality? if Any suggestions will be welcomed!
Thanks

Please go through
http://drf-haystack.readthedocs.io/en/latest/09_multiple_indexes.html
This post describes a way similar to what you are looking.
Basically you can specify a list of indexes in 'index_classes' attribute.
This can be useful when your search engine has indexed multiple models and you want to provide aggregate results across two or more of them. To use the default multiple index support, simply add multiple indexes the index_classes list.

Related

How could I create a docstring decorator in the presence of properties?

I have a collection of ever more specialized classes which correspond to collections of the same kind of data (temperature, density, etc) but for different drifts, for example, one subclass has dimensions (nx, ny) and a different suclass has dimensions (ncv), and I want to reflect that in the docstrings, for having a better documentation using Sphinx.
After reading many very useful threads here in Stack Overflow, I have arrived to this model:
import numpy as np
from functools import wraps
def class_decorator(cls):
import ipdb; ipdb.set_trace()
clsdict = {}
mro = cls.mro()
mro.reverse()
for tmp in mro[1:]: ##Ignore object class parent.
clsdict.update(tmp.__dict__)
for name, method in clsdict.items():
if hasattr(method, '__og_doc__'):
try:
method.__doc__ = method.__og_doc__.format(**clsdict)
except:
pass
else:
try:
method.__og_doc__ = method.__doc__
method.__doc__ = method.__doc__.format(**clsdict)
except:
pass
return cls
def mark_documentation(fn):
if not hasattr(fn, '__og_doc__'):
try:
fn.__og_doc__ = fn.__doc__
except:
pass
#wraps(fn)
def wrapped(*args, **kwargs):
return fn(*args, **kwargs)
return wrapped
def documented_property(fn):
if not hasattr(fn, '__og_doc__'):
try:
fn.__og_doc__ = fn.__doc__
except:
pass
#wraps(fn)
def wrapped(*args, **kwargs):
return fn(*args, **kwargs)
prp= property(wrapped)
prp.__og_doc__ = fn.__og_doc__
return prp
#class_decorator
class Base(object):
_GRID_DIM = 'nx, ny'
_TYPE = 'BaseData'
def __init__(self, name):
self.name = name
def shape(self):
""" This docstring contains the type '{_TYPE}' of class."""
print('Simple')
def operation(self, a, b, oper=np.sum, **kwargs):
""" Test for functions with args and kwargs in {_TYPE}"""
return oper([a,b])
#classmethod
def help(cls, var):
try:
print(get(cls, var).__doc__)
except:
print("No docstring yet.")
#class_decorator
class Advanced(Base):
_GRID_DIM = 'ncv'
_TYPE = 'AdvancedData'
def __init__(self,name):
super().__init__(name)
#property
#mark_documentation
# #documented_property
def arkansas(self):
"""({_GRID_DIM}, ns): Size of Arkansaw."""
return 'Yeah'
I am aiming to get the correctly formatted docstring when I call the help method or I use Sphinx, so that:
> adv = Advanced('ADV')
> adv.help("arkansas")
(ncv, ns): Size of Arkansaw.
> adv.help("operation")
Test for functions with args and kwargs in AdvancedData
I have managed to make it work so far, except for properties, because I assigned __og_doc__ to the function, but the property does not have that attribute. My last attempt at monkeypatching this, documented_property, fails because property is inmutable (as expected), and I cannot come up with any way to avoid this roadblock.
Is there any way around this problem?

Getting a value from a Class Object in Python based on User Input

so I'm in the process of learning Python, so forgive any naivety.
I'm doing some practice on Classes - and I'm making it so that when a user input their car - it looks for instantiated objects and then returns the price.
I get it to work okay for "BMW" - but when I try Ferrari - it only return the price for the BMW (20k). I'm thinking it is something not right with the price_check function part of the code.
Please could you provide some guidance here? Code below:
class Car:
car_list = []
def __init__(self, make, model, price):
self.make = make
self.model = model
self.price = price
self.car_list.append(self)
#this is the part of the code that i'm stuck with
def price_check(self):
for i in Car.car_list:
if New_Car.make == self.make:
return i.price
else:
print("Not available")
BMW = Car('BMW', '1 Series', "£20,000")
Ferrari = Car('Ferrari', 'Italia', "£90,000")
New_Car = Car(
input("What is make of your car? "), input("What is the model? "), "")
print("The cost of this car is: ", New_Car.price_check())
So essentially, I want it to return the price of the Ferrari if that's what the user typed.
Thanks in advance. And, sorry for any incorrect formatting, etc...
Okay, I agree with the comments made by #Jarvis regarding errors in your code, I would also add that in Cars init the price is a required variable and should cause an error on instantiation. In addition, in price_check, since the new_car instance has already been added to the list, price_check will also examine that entry and either find None or 0 price, so will never get to the "No Price Available" return. Here's how I would implement the Class
class Car:
car_list = []
def __init__(self, make, model, price=None): #makes providing price optional
self._make = make
self._model = model
self._price = price
Car.car_list.append(self)
#property
def make(self):
return self._make
#property
def model(self):
return self._model
#property
def price(self):
return self._price
#price.setter
def price(self, val):
self._price = val
def price_check(self):
for i in Car.car_list:
if i != self and self.make == i.make and self.model == i.model:
return i.price
return "Not available"
Two issues, first you need to append to the list not bound to your instance but the one bound to your class as a class-attribute:
def __init__(self, make, model, price):
self.make = make
self.model = model
self.price = price
# use the 'class' rather than the 'instance', you need to modify a class-attribute
Car.car_list.append(self)
Second, your issue in price check
def price_check(self):
for i in Car.car_list:
# you need to compare self's make with 'i.make' (elements in list)
if self.make == i.make:
return i.price
else:
print("Not available")

How to overload python sets to accept duplicates?

I had taken a python coding test, which asked to create a class that overloads the builtin set(all the methods of sets must work). The only change between the set I was asked to create and the builtin sets is that my custom sets SHOULD store duplicates, and 2 more custom methods.
Here is what I could come up with:
import builtins
class Multiset(builtins.set):
def __init__(self):
super().__init__()
self.my_set = builtins.set()
def add(self, val):
self.my_set.add(val)
def remove(self, val):
# removes one occurrence of val from the multiset, if any
self.my_set.discard(val)
def __contains__(self, val):
# returns True when val is in the multiset, else returns False
return val in self.my_set
def __len__(self):
# returns the number of elements in the multiset
return len(self.my_set)
I have tried overriding multiple methods, but to no avail. I also couldn't find a method that defined this non-duplicate criteria for sets. So, how do I do this?
EDIT 1:
Here is the problem description, if you want to see it.
You can use a dictionary which maps objects to a list of all objects that are equal to themselves. The advantage is that dict keys are already set-like.
from collections import defaultdict
class MultiSet:
def __init__(self):
self._items = defaultdict(list)
def add(self, item):
self._items[item].append(item)
def remove(self, item):
try:
self._items[item].remove(item)
except ValueError:
pass
def __contains__(self, item):
return item in self._items
def __len__(self):
return sum(len(v) for v in self._items.values())

Is it possible to involve a search bar on a DataFrame in Pandas?

I have a DataFrame in Pandas which collects some data from an Excel document. I created a GUI with PyQt5 in order to make it look more interesting but here is the thing.
Is it possbile to make a dynamic search bar in order to search through that DataFrame? For example, my DataFrame has over 3k+ rows and I wanna search for John Doe, then the results will come up on the GUI. As far as I know, QLineEdit is used for this but I can't seem to implement it on my code.
Is it me that is doing wrong or it is not possible to do it on a DataFrame? And if anyone wanna help me, just let me know, I would be so grateful and thankful, I guess it'll only take 10-15 minutes. I can also post the code here, but talking on Discord and explaining you in detail and also sharing screens would be a lot easier.
This can be done by subclassing QAbstractTableModel to create a custom table model that uses the underlying dataframe for supplying data to a QTableView. This custom model can then be combined with a QProxyFilterSortModel to filter the data in the table. To create a custom non-editable model from QAbstractTableModel you need to implement rowCount, columnCount, data, and headerData at the very least. In this case, minimal implemetation could be something like this:
class DataFrameModel(QtCore.QAbstractTableModel):
def __init__(self, data_frame, parent = None):
super().__init__(parent)
self.data_frame = data_frame
def rowCount(self, index):
if index.isValid():
return 0
return self.data_frame.shape[0]
def columnCount(self, index):
if index.isValid():
return 0
return self.data_frame.shape[1]
def data(self, index, role):
if not index.isValid() or role != QtCore.Qt.DisplayRole:
return None
return str(self.data_frame.iloc[index.row(), index.column()])
def headerData(self, section, orientation, role=None):
if role != QtCore.Qt.DisplayRole:
return None
if orientation == QtCore.Qt.Vertical:
return self.data_frame.index[section]
else:
return self.data_frame.columns[section]
To show and filter the data in a table you could do something like this:
class MyWidget(QtWidgets.QWidget):
def __init__(self, parent = None):
super().__init__(parent)
self.table_view = QtWidgets.QTableView()
self.proxy_model = QtCore.QSortFilterProxyModel()
# by default, the QSortFilterProxyModel will search for keys in the first column only
# setting QSortFilterProxyModel.filterKeyColumn to -1 will match values in all columns
self.proxy_model.setFilterKeyColumn(-1)
self.table_view.setModel(self.proxy_model)
# line edit for entering (part of) the key that should be searched for
self.line_edit = QtWidgets.QLineEdit()
self.line_edit.textChanged.connect(self.filter_text)
vlayout = QtWidgets.QVBoxLayout(self)
vlayout.addWidget(self.line_edit)
vlayout.addWidget(self.table_view)
def filter_text(self, text):
self.proxy_model.setFilterFixedString(text)
def set_data(self, data):
self.model = DataFrameModel(pd.DataFrame(data))
self.proxy_model.setSourceModel(self.model)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
win = MyWidget()
win.set_data({'a':['apple', 'banana', 'cherry'], 'b':[4,5,6], 'c':['green', 'yellow', 'red']})
win.show()
app.exec()
Of course this is a very basic implementation but I might help you get started.
I think the library pandas_ui is what you are looking for here.
It allows you to play with your dataframe interactively, i.e. in a microsoft excel fashion. Particularly, the column filtering option should do what you want.

PyQt and ListModel

How can I fill a data list in a data listmodel with the content of a given path?
Here is what I'm trying to do :
class TestListModel(QAbstractListModel):
def __init__(self, parent=None):
QAbstractListModel.__init__(self, parent)
self.testnames = []
def load_docfiles(self):
cfg = Config()
for filename in glob.glob(os.path.join(cfg.test_path, 'test_*.rst')):
self.testnames = os.path.basename(filename)[5:-4]
filepath = os.path.abspath(filename)
print "load docfile", str(self.testnames)
return str(self.testnames)
def rowCount(self, index):
return len(self.testnames)
def data(self, index, role):
if role == Qt.DisplayRole:
cfg = Config()
for filename in glob.glob(os.path.join(cfg.test_path, 'test_*.rst')):
self.testnames = os.path.basename(filename)[:-4]
filepath = os.path.abspath(filename)
return self.testnames
def columnCount(self, index):
pass
Another question is how can I load an html page (in a qwebview) associated with an item selected from my listview?
Thanking you!!
I think you are misunderstanding the purpose of the data method in the Model classes in Qt. Instead of reloading your data (in your case your list of file names), the data method should be mapping an index given by the associated QListView to an item in your testnames list. For example,
def data(self, index, role):
if role == Qt.DisplayRole:
testname = self.testnames[index.row()]
return testname
It looks like you want your load_docfiles method to store a list of file names in self.testnames. You could rewrite it like this:
def load_docfiles(self):
cfg = Config()
for filename in glob.glob(os.path.join(cfg.test_path, 'test_*.rst')):
self.testnames.append(os.path.basename(filename)[5:-4])
filepath = os.path.abspath(filename)
print "load docfile", str(self.testnames)
Your class could then be called from your main application like this:
self.view = QtGui.QListView(self)
self.model = TestListModel()
self.model.load_docfiles()
self.view.setModel(self.model)

Resources