I'm trying to create notifications like facebook has. Everything works all right but I have duplicates. For instance, action = like, url = post/1 I want to get all notifications with status = 1 - unread and eliminate duplicates where action and url are the same. You can find code below I have such error:
error: 'list index out of range' in
if n_dup[i]['url'] == n_dup[j]['url'] and n_dup[i]['action'] == n_dup[j]
def recieve_notification(request):
t = loader.get_template('notifications.html')
nots = Notification.objects.filter(recipent=request.user, status=1, pub_date__gte=datetime.datetime.now()-datetime.timedelta(days=3))
n_dup = [] #list of notifications with duplicates
for n in nots:
n_dup.append({'id':n.id, 'url':n.url, 'action':n.action})
i = len(n_dup)-1
j = len(n_dup)-1
while j>=0:
while i>=0:
if n_dup[i]['url'] == n_dup[j]['url'] and n_dup[i]['action'] == n_dup[j]['action'] and i is not j:
del n_dup[i]
i-=1
j-=1
out_n = []
for n in n_dup:
n_id = n['id']
out_n.append(Notification.objects.get(id=n_id))
c = RequestContext(request, {'notifications':out_n, 'notifications_count':len(out_n)})
return HttpResponse(t.render(c))`
Maybe you are in knowledge of better way to code all this stuff?
On the first iteration of both loops, j == i == len(n_dup)-1, so n_dup[i] == n_dup[j]. It is considered a duplicate and is remove. On the second iteration, you'll try to access n_dub[len(n_dup)-1] which doesn't exist any more, because you removed it.
If I may suggest an alternative approach, lets be lazy and have python do the duplicate detection for us :
class Notification:
def __init__(self, id, url, action):
self.id = id
self.url = url
self.action = action
def __eq__(self, other):
return self.url == other.url and self.action == other.action
def __hash__(self):
return hash(self.url) ^ hash(self.action)
unique_notifications = {Notification(n.id, n.url, n.action) for n in nots}
We define a notification object with a way to compare it and compute a hash (this is needed to put it in a set), and create a set of the notifications. A set never contains duplicate, so you can now iterate over the set !
You can also add this methods to your notification object and use it directly. You would then write :
out_n = set(Notification.objects.filter(...))
Bonus : the algorithm used by the set to remove duplicate is much more efficient than the one you where using.
Related
I am trying to loop through my One2Many records to avoid duplication.
class sales_target(models.Model):
_name = 'sales.target'
_description = 'Sales Target'
name = fields.Char(string='Name',required=True)
from_date = fields.Date(string='From Date',required=True)
to_date = fields.Date(string='To Date',required=True)
sales_team = fields.Many2one('crm.team',required=True)
sales_record_ids = fields.One2many('sales.target.record','sales_target_rec_id',string='Sales Record')
#api.one
def check_duplication(self,result):
count = 0
if self.sales_record_ids:
for record in self.sales_record_ids:
if result.id == record.sales_person_p_id:
count = 1
if count == 0:
self.write({'sales_record_ids':[(0,0,{'sales_person':result.name})]})
#api.one
def get_sales_person(self):
for res in self.sales_team.member_ids:
self.check_duplication(res)
The other class is as:
class sales_target_record(models.Model):
_name = 'sales.target.record'
sales_target_rec_id = fields.Many2one("sales.target")
sales_person = fields.Char(string='Sales Person',readonly=True,required=True)
sales_person_p_id = fields.Char(compute='get_value',store=True)
#api.onchange('sales_person')
#api.depends('sales_person')
def get_value(self):
res = self.env['res.partner'].search([('name','=',self.sales_person)])
self.sales_person_p_id = res[0].id
Now when I am hitting the button i still have duplicate records. However I tried to compare with name and things work good but I cannot compare with names since its not correct because names can be same but id cannot. That function was as:
#api.one
def check_duplication(self,result):
count = 0
if self.sales_record_ids:
for record in self.sales_record_ids:
if result.name == record.sales_person:
count = 1
if count == 0:
self.write({'sales_record_ids':[(0,0,{'sales_person':result.name})]})
Hope for guidance on this.
Can you try like this
#api.multi
def check_duplication(self,result):
if self.sales_record_ids:
for record in self.sales_record_ids:
if not result.name == record.sales_person:
self.write({'sales_record_ids':[(0,0,{'sales_person':result.name})]})
Concluding from the fact that for name it works properly, something might be wrong with your if condition.
sales_person_p_id is of type char, however you seem to compare it with an integer: result.id.
Have you made sure that both objects in your if condition are of the same type?
Try to make sales_person_p_id an integer field (e.g. via sales_person_p_id = fields.Integer(compute='get_value',store=True) or do some kind of type casting before comparing the objects.
In my database I have rooms, these rooms have multiple boolean values like has_tv.
A user can search for a room in a city, he will be redirected to the result page, where he finds all rooms in the searched city.
Now there is a filter function which filters the current result set, by rooms which for example have a tv. For this the user checks a checkbox with the value has_tv
I managed to make it work, but it does not ignore False values. this means if a user check has_tv it will be true but all the other boolean values will be false (because they are unchecked). So this will show me only results where has_tv is true and all others false, I need to see results where has_tv is true and others are irrelevant, some can be false and true.
Thats what I use atm:
if form.validate_on_submit():
all_rooms_in_city = Zimmer.query.filter(or_(Zimmer.haustiere_erlaubt.is_(form.haustiere_erlaubt.data), Zimmer.bettwaesche_wird_gestellt.is_(form.bettwaesche_wird_gestellt.data))).all()
else:
all_rooms_in_city = Zimmer.query.order_by(desc("stadt")).all()
That are all values which can be checked (there are a lot, so hardcoding all possibilites is impossible):
class FilterZimmerForm(Form):
haustiere_erlaubt = BooleanField("Haustiere")
bettwaesche_wird_gestellt = BooleanField("Bettwaesche")
grill_vorhanden = BooleanField("grill_vorhanden")
safe_vorhanden = BooleanField("safe_vorhanden")
kuehlschrank_vorhanden = BooleanField("kuehlschrank_vorhanden")
rauchen_erlaubt = BooleanField("rauchen_erlaubt")
parkplatz_vorhanden = BooleanField("parkplatz_vorhanden")
kochmoeglichkeit_vorhanden = BooleanField("kochmoeglichkeit_vorhanden")
restaurant_im_haus_vorhanden = BooleanField("restaurant_im_haus_vorhanden")
handtuecher_werden_gestellt = BooleanField("handtuecher_werden_gestellt")
tv_vorhanden = BooleanField("tv_vorhanden")
waschmoeglichkeit_vorhanden = BooleanField("waschmoeglichkeit_vorhanden")
wlan_vorhanden = BooleanField("wlan_vorhanden")
Of course later if lets say two of them are checked, it shall show all rooms where the two checked values are true and all others shall not matter!
Here is a screenshot of the website where the function is needed:
EDIT:
It seems I found a solution, have tested a few times and it seems to work as intended but it does not feel like it is best practise:
if form.validate_on_submit():
filter_result = []
if form.haustiere_erlaubt.data == True:
filter_result.append(Zimmer.haustiere_erlaubt.is_(True))
if form.bettwaesche_wird_gestellt.data == True:
filter_result.append(Zimmer.bettwaesche_wird_gestellt.is_(True))
if form.grill_vorhanden.data == True:
filter_result.append(Zimmer.grill_vorhanden.is_(True))
if form.safe_vorhanden.data == True:
filter_result.append(Zimmer.safe_vorhanden.is_(True))
if form.kuehlschrank_vorhanden.data == True:
filter_result.append(Zimmer.kuehlschrank_vorhanden.is_(True))
if form.rauchen_erlaubt.data == True:
filter_result.append(Zimmer.rauchen_erlaubt.is_(True))
if form.parkplatz_vorhanden.data == True:
filter_result.append(Zimmer.parkplatz_vorhanden.is_(True))
if form.kochmoeglichkeit_vorhanden.data == True:
filter_result.append(Zimmer.kochmoeglichkeit_vorhanden.is_(True))
if form.restaurant_im_haus_vorhanden.data == True:
filter_result.append(Zimmer.restaurant_im_haus_vorhanden.is_(True))
if form.handtuecher_werden_gestellt.data == True:
filter_result.append(Zimmer.handtuecher_werden_gestellt.is_(True))
if form.tv_vorhanden.data == True:
filter_result.append(Zimmer.tv_vorhanden.is_(True))
if form.waschmoeglichkeit_vorhanden.data == True:
filter_result.append(Zimmer.waschmoeglichkeit_vorhanden.is_(True))
if form.wlan_vorhanden.data == True:
filter_result.append(Zimmer.wlan_vorhanden.is_(True))
for item in filter_result:
all_rooms_in_city = Zimmer.query.filter(item).all()
else:
all_rooms_in_city = Zimmer.query.order_by(desc("stadt")).all()
First if you want to avoid hardcoding you need some way to get programatically which attributes you want to use to filter and what the appropriate field in the form is called. I guess you could scan the Zimmer class and get the name of all boolean attributes, but for now I simply asume a list of strings with the name of the attribute where in the form the name is the same:
filter_list = ["haustiere_erlaubt", ...]
Then you need to add the filters to the query only if the form is true. So:
if form.validate_on_submit():
query = Zimmer.query
for filter_name in filter_list:
if getattr(form, filter_name).data:
query = query.filter(getattr(Zimmer, filter_name).is_(True)))
all_rooms_in_city = query.all()
I guess you could also build a dictionary of the appropriate filters and use filter_by:
filter_dict = { filter_name: True for filter_name in filter_list if getattr(form, filter_name).data }
all_rooms_in_city = Zimmer.query.filter_by(**filter_dict).all()
In your code you are redefining all_rooms_in_the_city in the for loop for each item, so only the last filter will actually apply.
As part of a beginners' university Python project, I am currently creating a database of words, be it Nouns, Verbs, Determiners, Adjectives.
Now the problem I am having is that the words being read into the program via the lexicon.readfromfile method are being put into the dictionary via an instance of a class ( be it noun, verb or adjective ). This created the problem that I have absolutely no idea how to call these objects from the dictionary since they do not have variables as keys, but rather memory locations (see the following):
{<__main__.Verb object at 0x02F4F110>, <__main__.Noun object at 0x02F4F130>, <__main__.Adjective object at 0x02F4F1D0>, <__main__.Noun object at 0x02F4F170>}
Does anyone have any idea how I can call these keys in such a way that I can make them usable in my code?
Here is the part I'm stuck on:
Add a method getPast() to the Verb class, which returns the past tense of the Verb. Your getPast() method can simple work by retrieving the value of ‘past’ from the attributes.
Here is a the majority of the code, leaving out the Noun and Adjective classes:
class Lexicon(object):
'A container clas for word objects'
def __init__(self):
self.words = {}
def addword(self, word):
self.words[word.stringrep] = word
def removeword(self, word):
if word in self.words:
del(word)
print('Word has been deleted from the Lexicon' )
else:
print('That word is not in the Lexicon')
def getword(self,wordstring):
if wordstring in self.words:
return self.words[wordstring]
else:
return None
def containsword(self,string):
if string in self.words:
return True
else:
return False
def getallwords(self):
allwordslist = []
for w in self.words:
allwordslist.append(self.words[w])
return set(allwordslist)
def readfromfile(self, x):
filehandle = open(x, 'r')
while True:
line = filehandle.readline()
if line == '':
break
line = line.strip()
info = line.split(',')
if info[1] == 'CN' or info[1] == 'PN':
noun=Noun(info[0],info[1])
noun.setattribute('regular',bool(info[2]))
self.addword(noun)
elif info[1] == 'A':
adjective=Adjective(info[0],info[1])
adjective.setattribute('comparative', bool(info[2]))
self.addword(adjective)
elif info[1] == 'V':
verb=Verb(info[0],info[1])
verb.setattribute('transitive', bool(info[2]))
verb.setattribute('past', info[3])
self.addword(verb)
def writetofile(self, x):
filehandle = open(x, 'w')
for t in self.words.values():
filehandle.write(t.getFormattedString() + '\n')
filehandle.close()
#---------------------------------------------------------------------------#
class Word(object):
'A word of any category'
def __init__(self,stringrep,category):
self.wordattribute = {}
self.stringrep = stringrep
self.category = category
def setattribute(self, attributename, attributevalue):
self.wordattribute[attributename] = attributevalue
def getvalue(self,name):
if name in self.wordattribute:
return self.wordattribute[name]
else:
return none
def __str__(self):
return self.stringrep + ':' + self.category
def __lt__(self,otherword):
return self.stringrep < otherword.stringrep
class Verb(Word):
'"Represents a Verb."'
def __init__(self, stringrep, category):
super().__init__(stringrep,category)
def istransitive(self):
return self.transitive
def getFormattedString(self):
n = '{stringrep},{category}'
n = n.format(stringrep=self.stringrep, category=self.category)
for i in range(1,2):
for v,b in self.wordattribute.items():
n = n+','+str(b)
return n
You have a set there, not a dictionary. A set will let you check to see whether a given instance is in the set quickly and easily, but, as you have found, you can't easily get a specific value back out unless you already know what it is. That's OK because that's not what the set is for.
With a dictionary, you associate a key with a value when you add it to the dictionary. Then you use the key to get the value back out. So make a dictionary rather than a set, and use meaningful keys so you can easily get the value back.
Or, since I see you are already making a list before converting it to a set, just return that; you can easily access the items in the list by index. In other words, don't create the problem in the first place, and you won't have it.
class ModelCount(db.Model):
type = db.StringProperty(required=True,default='Default-type')
count = db.IntegerProperty(required=True, default=0) #Current counter
def to_dict(self):
d = dict([(p, unicode(getattr(self, p))) for p in self.properties()])
d["id"] = self.key().id()
return d
#Increments counter
#staticmethod
def increment_counter(en_name):
modelCount = ModelCount.all().filter('type',en_name).get()
if modelCount:
modelCount.count += 1
modelCount.put()
else:
modelCount = ModelCount(type=en_name, count=1)
modelCount.put()
In the above code (increment_counter), I am reading the count from ModelCount and incrementing it by one. I face run condition in increment_counter method when the server receives multiple requests. So I want to make increment_counter atomic. If I use #db.transactional on increment_counter, I get "Only ancestor queries are allowed inside transactions" error.
How can I fix this and make it atomic?
you could try to use sharding https://developers.google.com/appengine/articles/sharding_counters
full source available at https://github.com/GoogleCloudPlatform/appengine-sharded-counters-python
class MySong:
_songTitle = "Song Title"
_artistName = "Artist Name"
_likeIndicator = -1
def setTitleAndArtist(self, songTitle, artistName):
self._songTitle = songTitle
self._artistName = artistName
def setLike(self, likeIndicator):
self._likeIndicator = likeIndicator
def undoSetLike(self, songTitle):
Null
def getTitle(self):
return self._songTitle
def getArtist(self):
return self._artistName
def getLikeIndicator(self):
return self._likeIndicator
class MyPlaylist:
_mySongs = []
def add(self, song):
self._mySongs.append(song)
def showTitles(self):
index = 0
titlesList = []
while index != len(self._mySongs):
titlesList.append(self._mySongs[index].getTitle())
index = index + 1
return titlesList
def remove(self):
remindex = 0
while remindex != len(self._mySongs):
if (self._mySongs[index].getTitle()) == remChoice :
return("Song FOUND debug!")
self._mySongs.remove(index)
else:
remindex = remindex + 1
return("Song NOT FOUND debug!")
def getMySong(self):
Null
There is a list of song objects inside of _mySongs = []. I'm trying to remove one, based on the title variable of that object.
In a separate (unshown) part of the program, the user is asked to enter the title of the song they want removed as a string. This is saved as remChoice.
I'm not entirely sure how to remove the song based on the title.
I've tried for a while to get it going, obviously we find the index of the song in the list by matching it to the title (by calling the getTitle method), then removing that index when it's found.
This isn't working. Where am I going wrong?
If you want to delete an item from a list knowing it's index use:
del xs[i]
Where i is the index. (e.g: Your song's index based on your search).
list.remove() is used for removing a matching element form the list not the "ith" item.
You might also find that a list is not a suitable data structure here? Perhaps you could try storing key/value pairs in a dict. e.g:
my_songs = {}
my_aongs["My Song Title"] = MySong(title, description, length)
You can later delete songs via their keys:
del my_songs["My Song Title"]
where titles are your keys. This saves you from doing O(n) searching.
Update:
Your .remove() method should look more like the following:
def remove(self, title):
for i, song in enumerate(self._mySongs):
if song.getTitle() == title:
del self._mySongs[i]
return
print("Song not found!")
Here we're using list's iteration protocol by using a for x in xs: rather than using a while loop and doing manual bookkeeping. The builtin function enumerate() is also used to give us an index into the list we're iterating over (i.e: it's position in the sequence).
try
self._mySongs.remove(title)
That should work.
(Or from another object: replace self by whatever your object name is)