Odoo 8: Browse another module in method that runs on install/upgrade - odoo-8

I have written a module, which requires initial data population when it is installed/upgraded. The method (_initialize_history_prices) fires up successfully, but current_price_records does not seem to have any values, so it does nothing (the table has thousands of records). I see no errors while it is running. Is there something wrong I'm doing, or is it not possible to browse other modules during module install/upgrade and I should resort to SQL?
Here's the code with irrelevant parts truncated for brevity
class pricelist_partnerinfo_history(models.Model):
_name = 'pricelist.partnerinfo.history'
#api.model
def _initialize_history_prices(self):
'''Add missing current prices in historical records.'''
current_price_records = self.env['pricelist.partnerinfo'].browse()
for rec in current_price_records:
# Do stuff
pricelist_history_init.xml
<?xml version="1.0"?>
<openerp>
<data>
<!-- Initialize price list history records with current prices if they are missing in history -->
<function model="pricelist.partnerinfo.history" name="_initialize_history_prices"/>
</data>
</openerp>
__openerp__.py
'depends': ['product', 'purchase_automation'],
'data': [
'pricelist_history_init.xml',
'pricelist_view.xml',
],

in _initialize_history_prices() method, in current_price_records you will get empty records set of pricelist.partnerinfo because browse() without ids will return empty recordset, so when this method will call nothing will happen
to get all records you can use search() method
#api.model
def _initialize_history_prices(self):
'''Add missing current prices in historical records.'''
current_price_records = self.env['pricelist.partnerinfo'].search([])
for rec in current_price_records:
# Do stuff

Related

Kv related question - How to bound an on_press/release function to the viewclass of the recycleview?

I've been working on a project which required me to learn kv.
what I'm trying to do is use recycleview to display a list of people that are a part of a dataset I built and allow easy edit of the dataset.
what I've done is read the documentation and simply use the first example from there (with a slight change, the viewclass being a togglebutton:
[The Example][1]
so as for my question, what I want to do is simply bound an on_press/release function to the viewclass objects, for example what I want to do is to bound a function to all of the toggle buttons which appends the button's text to a list when It's being pressed and removes the name from the list when It's being released.
[1]: https://i.stack.imgur.com/55FlM.png
You can do that by adding the on_press to the data:
class RV(RecycleView):
def __init__(self, **kwargs):
self.list = []
super(RV, self).__init__(**kwargs)
self.data = [{'text': str(x), 'on_press':partial(self.toggle, str(x))} for x in range(100)]
def toggle(self, name):
print('toggle')
if name in self.list:
self.list.remove(name)
else:
self.list.append(name)
print('list is now:', self.list)

Ruby Aws::Route53::Client filter for objects without tags

I am gathering up untagged health checks using the ruby Aws::Route53::Client with two functions and then tagging them. Right now I am gathering all health checks, then filtering for ones without tags. Is there a filter I can used to directly gather all the health checks without tags?
Heres the snip of my code that currently does this:
...
# lookup checks, list_tags_for_resources has a max search of 10 items
checks_search = client.list_health_checks(marker: marker, max_items: 10)
ids = checks_search.to_h[:health_checks].map {|check| check[:id]}
list_check_tags = client.list_tags_for_resources({
resource_type: "healthcheck",
resource_ids: ids,
}).to_h
untagged = list_check_tags[:resource_tag_sets].select{|check| check[:tags].empty?}.map {|check| check[:resource_id]}
...
At the least is there a way to run list_tags_for_resources while only filtering for resources without any tags?
To do something within the list_tags_for_resources method to make it returned a filtered list, you'd have to monkey patch the method. You could do something like this (untested, probably incorrect, but shows the principle):
module Aws::Route53
class Client < Seahorse::Client::Base
def list_tags_for_resource(params = {}, options = {})
req = build_request(:list_tags_for_resource, params)
req.send_request(options)[:resource_tag_sets].select{|check| check[:tags].empty?}.map {|check| check[:resource_id]}
end
end
end

Concept for recipe-based parsing of webpages needed

I'm working on a web-scraping solution that grabs totally different webpages and lets the user define rules/scripts in order to extract information from the page.
I started scraping from a single domain and build a parser based on Nokogiri.
Basically everything works fine.
I could now add a ruby class each time somebody wants to add a webpage with a different layout/style.
Instead I thought about using an approach where the user specifies elements where content is stored using xpath and storing this as a sort of recipe for this webpage.
Example: The user wants to scrape a table-structure extracting the rows using a hash (column-name => cell-content)
I was thinking about writing a ruby function for extraction of this generic table information once:
# extracts a table's rows as an array of hashes (column_name => cell content)
# html - the html-file as a string
# xpath_table - specifies the html table as xpath which hold the data to be extracted
def basic_table(html, xpath_table)
xpath_headers = "#{xpath_table}/thead/tr/th"
html_doc = Nokogiri::HTML(html)
html_doc = Nokogiri::HTML(html)
row_headers = html_doc.xpath(xpath_headers)
row_headers = row_headers.map do |column|
column.inner_text
end
row_contents = Array.new
table_rows = html_doc.xpath('#{xpath_table}/tbody/tr')
table_rows.each do |table_row|
cells = table_row.xpath('td')
cells = cells.map do |cell|
cell.inner_text
end
row_content_hash = Hash.new
cells.each_with_index do |cell_string, column_index|
row_content_hash[row_headers[column_index]] = cell_string
end
row_contents << [row_content_hash]
end
return row_contents
end
The user could now specify a website-recipe-file like this:
<basic_table xpath='//div[#id="grid"]/table[#id="displayGrid"]'
The function basic_table is referenced here, so that by parsing the website-recipe-file I would know that I can use the function basic_table to extract the content from the table referenced by the xPath.
This way the user can specify simple recipe-scripts and only has to dive into writing actual code if he needs a new way of extracting information.
The code would not change every time a new webpage needs to be parsed.
Whenever the structure of a webpage changes only the recipe-script would need to be changed.
I was thinking that someone might be able to tell me how he would approach this. Rules/rule engines pop into my mind, but I'm not sure if that really is the solution to my problem.
Somehow I have the feeling that I don't want to "invent" my own solution to handle this problem.
Does anybody have a suggestion?
J.

Creating new class instances inside an array without overwriting existing ones

I've currently got a system that involves quite a lot of new class instances, so I've had to assign them using an array, as was suggested here: Create and initialize instances of a class with sequential names
However, I'll have to be constantly adding new instances whenever a new one appears, without overwriting existing ones. Might some validation and a modified version of my existing code be the best option?
This is my code, currently every time it runs, the existing data is overwritten. I want the status to be overwritten if it's changed, but I also want to be able to store one or two variables in there permanently.
E2A: Ignore the global variables, they're just there for testing.
$allids = []
$position = 0 ## Set position for each iteration
$ids.each do |x| ## For each ID, do
$allids = ($ids.length).times.collect { MyClass.new(x)} ## For each ID, make a new class instance, as part of an array
$browser.goto("http://www.foo.com/#{x}") ## Visit next details page
thestatus = Nokogiri::HTML.parse($browser.html).at_xpath("html/body/div[2]/div[3]/div[2]/div[3]/b/text()").to_s ## Grab the ID's status
theamount = Nokogiri::HTML.parse($browser.html).at_xpath("html/body/div[2]/div[3]/div[2]/p[1]/b[2]/text()").to_s ## Grab a number attached to the ID
$allids[$position].getdetails(thestatus, theamount) ## Passes the status to getdetails
$position += 1 ## increment position for next iteration
end
E2A2: Gonna paste this from my comment:
Hmm, I was just thinking, I started off by making the previous values dump into another variable, then another variable grabs the new values, and iterates over them to see if any match the previous values. That's quite a messy way to do it though, I was thinking, would self.create with a ||= work? – Joe 7 mins ago
If I understand you correctly, you need to store status and amount for each ID, right? If so, then something like this would help you:
# I'll store nested hash with class instance, status and amount for each id in processed_ids var
$processed_ids = {}
$ids.each do |id|
processed_ids[id] ||= {} #
processed_ids[id][:instance] ||= MyClass.new(id)
processed_ids[id][:status] = get_status # Nokogiri method
processed_ids[id][:amount] = get_amount # Nokogiri method
end
What does this code do: it only once creates instance of your class for each id, but always updates its status and amount.

How to get django ModelForm to not clean a ManyToManyField before running clean_ for that field?

class MyModel(models.Model) :
people = models.ManyToManyField(User,related_name='people')
...
class MyForm(ModelForm) :
class Meta :
model = MyModel
widgets = {'people':TextInput(),}
def clean_people(self) :
# turn a comma-separated list of names into a Python list
return [name0,name1,...]
def clean(self) :
# if no other errors, turn list of names into list of Users
This doesn't work, because clean_people doesn't get called before field.clean gets called, where field is an instance of ModelMultipleChoiceField, which checks for a Python list or tuple and so raises a ValidationError and skips clean_people.
Is there a reason why the order of calls is the way it is, and is there some standard provision for avoiding this problem? I could set field.clean to lambda x:x in each instance of MyForm, but that seems really ugly. Is perhaps the right thing to do to explicitly define the people field in MyForm as a CharField ?
why don't you override clean's behaviour?
class MyForm(ModelForm):
class Meta:
model = MyModel
widgets = {'people':TextInput(),}
def clean_people(self):
# turn a comma-separated list of names into a Python list
return [name0,name1,...]
# override below!
def clean(self):
super(MyForm, self).clean()
# do stuff you need, eg: call clean_people(self)
return self.cleaned_data
I'm not sure I understood your problem 100%, but just by defining clean_people() before clean() doesn't mean that's going to be called before, or even called at all.
It seems that the right? thing is to define the people field in the form, overriding the model's idea of that field
class MyModel(models.Model) :
people = models.ManyToManyField(User,related_name='people')
...
class MyForm(ModelForm):
people = CharField(...)
class Meta :
model = MyModel
def clean_people(self) :
# turn a comma-separated list of names into a Python list
return [name0,name1,...]
def clean(self) :
# if no other errors, turn list of names into list of Users

Resources