Can anyone help please with the below:
I'm trying to divide two float fields but I'm getting an error
AttributeError: 'stock.picking' object has no attribute 'calculate_reserved_per'
here's my code:
from odoo import models, fields, api
class PickingTotalQty(models.Model):
_inherit = 'stock.picking'
sum_dmd_qty = fields.Float(compute='calculate_dmd_qty', string='Total Demand Quantity')
sum_reserved_qty = fields.Float(compute='calculate_reserved_qty', string='Total Reserved Quantity')
reserved_qty_per = fields.Float(compute='calculate_reserved_per', string='Reserved Quantity (%)')
def calculate_dmd_qty(self):
for rs in self:
dmdqty = 0
for line in rs.move_lines:
dmdqty += line.product_uom_qty
rs.sum_dmd_qty = dmdqty
def calculate_reserved_qty(self):
for rs in self:
reservedqty = 0
for line in rs.move_lines:
reservedqty += line.reserved_availability
rs.sum_reserved_qty = reservedqty
#api.depends('sum_reserved_qty', 'sum_dmd_qty')
def calculate_reserved_per(self):
for rec in self:
rec.reserved_qty_per = rec.sum_reserved_qty / rec.sum_dmd_qty
If you have already restarted the server check the indentation of the calculate_reserved_per method and make sure rec.sum_dmd_qty is different from Zero before dividing.
The strange behavior you are facing is the result of the indentation of the last line in both methods: calculate_dmd_qty and calculate_reserved_qty which ignore all records except the last one. In the form view, the method is called with a recordset containing one record, the current record.
To correct that, move the last line of each method inside the for-loop.
sum_dmd_qty and sum_reserved_qty are computed fields that depends on move_lines.product_uom_qty and move_lines.reserved_availability respectively and it should be used to specify when to recompute theire values using #api.depends.
You can use the accounting invoice _compute_amount as a reference and merge the three methods in one method that compute the value of the three fields.
Example:
#api.depends("move_lines.product_uom_qty", "move_lines.reserved_availability")
def _calculate_quantities(self):
for picking in self:
dmd_qty = 0
reserved_qty = 0
for move in picking.move_lines:
dmd_qty += move.product_uom_qty
reserved_qty += move.reserved_availability
picking.sum_dmd_qty = dmd_qty
picking.sum_reserved_qty = reserved_qty
if picking.sum_dmd_qty:
picking.reserved_qty_per = picking.sum_reserved_qty / picking.sum_dmd_qty
I did not use #api.one because it is not available in Odoo13
Related
I am attempting to query all rows for a column called show_id. I would then like to compare each potential item to be added to the DB with the results. Now the simplest way I can think of doing that is by checking if each show is in the results. If so pass etc. However the results from the below snippet are returned as objects. So this check fails.
Is there a better way to create the query to achieve this?
shows_inDB = Show.query.filter(Show.show_id).all()
print(shows_inDB)
Results:
<app.models.user.Show object at 0x10c2c5fd0>,
<app.models.user.Show object at 0x10c2da080>,
<app.models.user.Show object at 0x10c2da0f0>
Code for the entire function:
def save_changes_show(show_details):
"""
Save the changes to the database
"""
try:
shows_inDB = Show.query.filter(Show.show_id).all()
print(shows_inDB)
for show in show_details:
#Check the show isnt already in the DB
if show['id'] in shows_inDB:
print(str(show['id']) + ' Already Present')
else:
#Add show to DB
tv_show = Show(
show_id = show['id'],
seriesName = str(show['seriesName']).encode(),
aliases = str(show['aliases']).encode(),
banner = str(show['banner']).encode(),
seriesId = str(show['seriesId']).encode(),
status = str(show['status']).encode(),
firstAired = str(show['firstAired']).encode(),
network = str(show['network']).encode(),
networkId = str(show['networkId']).encode(),
runtime = str(show['runtime']).encode(),
genre = str(show['genre']).encode(),
overview = str(show['overview']).encode(),
lastUpdated = str(show['lastUpdated']).encode(),
airsDayOfWeek = str(show['airsDayOfWeek']).encode(),
airsTime = str(show['airsTime']).encode(),
rating = str(show['rating']).encode(),
imdbId = str(show['imdbId']).encode(),
zap2itId = str(show['zap2itId']).encode(),
added = str(show['added']).encode(),
addedBy = str(show['addedBy']).encode(),
siteRating = str(show['siteRating']).encode(),
siteRatingCount = str(show['siteRatingCount']).encode(),
slug = str(show['slug']).encode()
)
db.session.add(tv_show)
db.session.commit()
except Exception:
print(traceback.print_exc())
I have decided to use the method above and extract the data I wanted into a list, comparing each show to the list.
show_compare = []
shows_inDB = Show.query.filter().all()
for item in shows_inDB:
show_compare.append(item.show_id)
for show in show_details:
#Check the show isnt already in the DB
if show['id'] in show_compare:
print(str(show['id']) + ' Already Present')
else:
#Add show to DB
For querying a specific column value, have a look at this question: Flask SQLAlchemy query, specify column names. This is the example code given in the top answer there:
result = SomeModel.query.with_entities(SomeModel.col1, SomeModel.col2)
The crux of your problem is that you want to create a new Show instance if that show doesn't already exist in the database.
Querying the database for all shows and looping through the result for each potential new show might become very inefficient if you end up with a lot of shows in the database, and finding an object by identity is what an RDBMS does best!
This function will check to see if an object exists, and create it if not. Inspired by this answer:
def add_if_not_exists(model, **kwargs):
if not model.query.filter_by(**kwargs).first():
instance = model(**kwargs)
db.session.add(instance)
So your example would look like:
def add_if_not_exists(model, **kwargs):
if not model.query.filter_by(**kwargs).first():
instance = model(**kwargs)
db.session.add(instance)
for show in show_details:
add_if_not_exists(Show, id=show['id'])
If you really want to query all shows upfront, instead of putting all of the id's into a list, you could use a set instead of a list which will speed up your inclusion test.
E.g:
show_compare = {item.show_id for item in Show.query.all()}
for show in show_details:
# ... same as your code
I have a function for filling in many fields automatically with an api.onchange() decorator.
#api.onchange('nursery_morning', 'nursery_evening', 'responsible_partner')
def retrieve_responsible_nursery(self):
if self.nursery_morning or self.nursery_evening:
if self.responsible_partner:
if not self.resp_civility1 and not self.resp_name1 and not self.resp_cp1 and not self.resp_num1 \
and not self.resp_address1 and not self.resp_town1 \
and not self.resp_phone1 and not self.resp_phonemobile1:
self.resp_civility1 = self.responsible_partner.title.name
self.resp_name1 = self.responsible_partner.name
self.resp_cp1 = self.responsible_partner.zip_id.name
self.resp_num1 = self.responsible_partner.street_number_id.name
self.resp_address1 = self.responsible_partner.street_id.name
self.resp_town1 = self.responsible_partner.city_id.name
self.resp_phone1 = self.responsible_partner.phone
self.resp_phonemobile1 = self.responsible_partner.mobile
This function works, but I do not want the fields to change until the fields are saved in the database and not before.
Currently, the fields do not change once one of the fields listed are filled but not saved in database
Two ways you can approach this, it seems that you could change these fields to be related fields if they are only ever going to hold the values from a related record (responsible_partner).
resp_civility1 = fields.Char("Field Label", related="responsible_partner.title.name")
Editing this field will edit the linked record as well, so it will not be a good fit if you want to change this value at all, or you could set it as read-only if you don't want it to be edited from here.
Another way to do it would be to override the write method, this will write the values at the time of saving the record and would look like this:
#api.multi
def write(self, vals):
res = super(ModelName, self).write(vals)
if vals.get('nursery_morning') or vals.get('nursery_evening') or vals.get('responsible_partner'):
if res.nursery_morning or res.nursery_evening:
if res.responsible_partner:
if not res.resp_civility1 and not res.resp_name1 and not res.resp_cp1 and not res.resp_num1 \
and not res.resp_address1 and not res.resp_town1 \
and not res.resp_phone1 and not res.resp_phonemobile1:
res.resp_civility1 = res.responsible_partner.title.name
res.resp_name1 = res.responsible_partner.name
res.resp_cp1 = res.responsible_partner.zip_id.name
res.resp_num1 = res.responsible_partner.street_number_id.name
res.resp_address1 = res.responsible_partner.street_id.name
res.resp_town1 = res.responsible_partner.city_id.name
res.resp_phone1 = res.responsible_partner.phone
res.resp_phonemobile1 = res.responsible_partner.mobile
return res
This will first call the write method of the model, then it will check if the three fields are being written to, if so it will run the same logic from your onchange method. Replace ModelName with whatever you have named your model class.
Given PostgreSQL 9.2.10, Django 1.8, python 2.7.5, the following model:
class soapProdAPI(models.Model):
soap_id = models.PositiveIntegerField(primary_key=True)
soap_host = models.CharField(max_length=20)
soap_ip = models.GenericIPAddressField(default='0.0.0.0')
soap_asset = models.CharField(max_length=20)
soap_state = models.CharField(max_length=20)
And the following code:
tableProdSoap = soapProdQuery()
#periodic_task(run_every=timedelta(minutes=2))
def saveSoapProd():
tableProdSoap = soapProdQuery()
if tableProdSoap != None:
for item in tableProdSoap:
commit = soapProdAPI(soap_id=item[0], soap_host=item[1], soap_asset=item[2], soap_state=item[3])
commit.save()
saveSoapNullIP()
To answer Josué Padilla's question:
#task
def saveSoapNullIP():
missingIP = soapProdAPI.objects.filter(soap_ip='0.0.0.0')
if missingIP:
for record in missingIP:
if str(record.soap_host).lower().startswith('1a'):
fqdn = str(record.soap_host) + 'stringvaluehere'
elif str(record.soap_host).lower().startswith('1b'):
fqdn = str(record.soap_host) + 'stringvaluehere'
elif str(record.soap_host).lower().startswith('1c'):
fqdn = str(record.soap_host) + 'stringvaluehere'
else:
fqdn = str(record.soap_host) + 'stringvaluehere'
try:
hostIp = check_output('host %s' % fqdn, shell=True)
hostIp = hostIp.split()[-1]
except:
hostIp = '0.0.0.0'
record.soap_ip = hostIp
record.save(update_fields=['soap_ip'])
My soapProdQuery only returns these 4 fields where there is a 5th field in the model (soap_ip). I know it is probably not the best way to do it but I have a separate block of code that queries the db for None values in soap_ip runs a subprocess host on them and saves it back with the ip address (The number of rows returned/updated should get smaller each pass through, as opposed to putting the logic for doing a host lookup into the request/this celery task itself which would run every API request. I have tried this already, it takes FOREVER to return the completed data.). The soap API I query does not provide the IP or I would grab it that way obviously. This all runs as background tasks using celery to make it invisible/seamless to the web user.
The issue I run into is that every time the saveSoapProd() runs it overwrites the previous soap_ip field with '0.0.0.0' thus negating the work of my other function. The other issue is that I cannot force_insert or force_update as I need both functionalities with this. My question is this: is there a way to selectively update/insert at the same time and completely exclude doing anything to the soap_ip each time saveSoapProd() runs? Any and all help is greatly appreciated. Thank you in advance.
** EDIT 1 **
I may or may not have found a solution in update_or_create or get_or_create, however I am unsure on the exact usage. The docs have me slightly confused.
** EDIT 2 **
I guess get_or_create is a bust. Works first pass through but every save after that fails with this:
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "<console>", line 8, in saveSoapProd
File "/lib/python2.7/site-packages/django/db/models/base.py", line 690, in save
% ', '.join(non_model_fields))
ValueError: The following fields do not exist in this model or are m2m fields: soap_id
Here is the code:
#periodic_task(run_every=timedelta(minutes=2))
def saveSoapProd():
tableProdSoap = soapProdQuery()
if tableProdSoap != None:
for item in tableProdSoap:
obj, created = soapProdAPI.objects.get_or_create(soap_id=item[0], defaults={'soap_host': item[1], 'soap_asset': item[2], 'soap_state': item[3]})
if created == False:
commit = soapProdAPI(soap_id=item[0], soap_host=item[1], soap_asset=item[2], soap_state=item[3])
commit.save(update_fields=['soap_id', 'soap_host', 'soap_asset', 'soap_state'])
I will be honest, I am not entirely sure what is causing this error.
** EDIT 3/CURRENT SOLUTION **
I was able to resolve my own issue by modifying my model and my task function. The solution uses get_or_create, but you could easily extrapolate how to use update_or_create from the solution provided. See the selected answer below for a coded example.
** TLDR **
I want to do a .save() where it may need to do a insert for new records or update for changed records WITHOUT touching the soap_ip field (no insert_only or update_only).
I don't know if you already knew this, but you can override the save() function of your model.
class soapProdAPI(models.Model):
soap_id = models.PositiveIntegerField(primary_key=True)
soap_host = models.CharField(max_length=20)
soap_ip = models.GenericIPAddressField(default='0.0.0.0')
soap_asset = models.CharField(max_length=20)
soap_state = models.CharField(max_length=20)
# Override save
def save(self, *args, **kwargs):
if self.soap_ip != '0.0.0.0':
self.soap_ip = your_ip # Here you can get your old IP an save that instead of 0.0.0.0
EDIT
You are getting
ValueError: The following fields do not exist in this model or are m2m fields: soap_id
Because you are trying to update soap_id, that field is defined as your model's primary key, so it is immutable when updating. That's why it crashes when you do:
commit.save(update_fields=['soap_id', 'soap_host', 'soap_asset', 'soap_state'])
Try removing soap_id from update_fields.
Solved my own issue without modifying the save method by making the following changes to my model:
class soapProdAPI(models.Model):
soap_id = models.PositiveIntegerField(unique=True, null=False)
soap_host = models.CharField(max_length=20)
soap_ip = models.GenericIPAddressField(default='0.0.0.0')
soap_asset = models.CharField(max_length=20)
soap_state = models.CharField(max_length=20)
and my task:
def saveSoapProd():
tableProdSoap = soapProdQuery()
if tableProdSoap != None:
for item in tableProdSoap:
try:
obj, created = soapProdAPI.objects.get_or_create(soap_id=item[0], defaults={'soap_host': item[1], 'soap_asset': item[2], 'soap_state': item[3]})
if created == False:
obj.soap_host = item[1]
obj.soap_asset = item[2]
obj.soap_state = item[3]
obj.save(update_fields=['soap_host', 'soap_asset', 'soap_state'])
except:
continue
saveSoapMissingIP()
EDIT
Just noticed Josué Padilla's response, which was in fact part of my problem that I solved with this answer. Thank you to Josué for all of your help.
I am trying to get maya to check if the listed object is a blendshape node or not.
This is my code:
def bake(self, *args):
self.items["selection"] = cmds.ls(sl = True)
self.items["shapes"] = cmds.listRelatives(self.items["selection"], ad = True)
shapes = ()
for i in self.items["shapes"]:
bs = cmds.listConnections(i, type = "blendShape", exactType = True)
if cmds.objectType(bs, isType = "blendShape"):
print bs
It returns # Error: RuntimeError: file X:/Documents/maya/scripts\jtBakeCharacter.py line 16: No object name specified
Line 16 is: if cmds.objectType(bs, isType = "blendShape"):
Except that I AM specifying an object name, that object name is bs .. I have printed the result of bs and it has many objects listed. Many.
The code is redundant. You don't need most of the lines. The listConnections already ensures that you have only blendshapes. The exact problem is that you are calling something like:
cmds.objectType([])
for some of those extra shapes. And this is illegal. But mostly you code can be encapsulated as follows:
selected = cmds.ls(sl = True, dag=True ,shapes = True)
blends = cmds.listConnections(selected , type = "blendShape", exactType = True)
for item in blends:
print item
But this may not catch your intent perfectly, but shows how may extra steps you take. In reality you don't need the line if cmds.objectType(bs, isType = "blendShape"): for anything
Joojaa's answer is elegant, but you can get it down even shorter by using the default selection behavior:
blendshapes = cmds.ls(cmds.listHistory(pdo=True), type='blendShape') or []
for item in blendshapes:
print item
(In the quest to make it even shorter I'm not checking for the selection, so this one fails if nothing is selected).
PS: if you need to get to the blendshape from one of the upstream shapes, instead of the deformed shape, you can use listHistory (f=True)
You could try this:
from pymel.core import *
for obj in selected():
shapeNode = obj.getChildren()[0]
for output in shapeNode.outputs():
if nodeType(output) == "blendShape":
print obj, "is a blendshape"
I am currently trying to populate 2 fields. They are both already created within a table that I want to populate with data from existing feature classes. The idea is copy all data from desired feature classes that match a particular Project #. The rows that match the project # will copy over to blank template with the matching fields. So far all is good except I need to push the data from the OBJECT ID field and the Name of the Feature Class in to 2 fields within the table.
**def featureClassName(table_path):
arcpy.AddMessage("Calculating Feature Class Name...")
print "Calculating Feature Class Name..."
featureClass = "FeatureClass"
SDE_ID = "SDE_ID"
fc_desc = arcpy.Describe(table_path)
lists = arcpy.ListFields(table_path)
print lists
with arcpy.da.SearchCursor(table_path, featureClass = "\"NAME\"" + " Is NULL") as cursor:
for row in cursor:
print row
if row.FEATURECLASS = str.replace(row.FEATURECLASS, "*", fc):
cursor.updateRow(row)
print row
del cursor, row
else:
pass**
The Code above is my attempt, out of many to populate the field with the Name of the Feature class.
I have attemped to do the same with the OID.
**for fc in fcs:
print fc
if fc:
print "Making Layer..."
lyr = arcpy.MakeFeatureLayer_management (fc, r"in_memory\temp", whereClause)
fcCount = int(arcpy.GetCount_management(lyr).getOutput(0))
print fcCount
if fcCount > 0:
tbl = arcpy.CopyRows_management(lyr, r"in_memory\temp2")
arcpy.AddMessage("Checking for Feature Class Name...")
arcpy.AddMessage("Appending...")
print "Appending..."
arcpy.Append_management(tbl, table_path, "NO_TEST")
print "Checking for Feature Class Name..."
featureClassName(table_path)
del fc, tbl, lyr, fcCount
arcpy.Delete_management(r"in_memory\temp")
arcpy.Delete_management(r"in_memory\temp2")
else:
arcpy.AddMessage("Pass... " + fc)
print ("Pass... " + fc)
del fc, lyr, fcCount
arcpy.Delete_management(r"in_memory\temp")
pass**
This code is the main loop for the feature classes within the dataset that i create a new layer/table to use for copying the data to the table. The data for Feature Class Name and OID dont have data to push, so thats where I am stuck.
Thanks Everybody
You have a number of things wrong. First, you are not setting up the cursor correctly. It has to be a updateCursor if you are going to update, and you called a searchCursor, which you called incorrectly, by the way. Second, you used = (assignment) instead of == (equality comparison) in the line "if row.FEATURECLASS ... Then 2 lines below that, your indentation is messed up on several lines. And it's not clear at all that your function knows the value of fc. Pass that as an arg to be sure. Bunch of other problems exist, but let's just give you an example that will work, and you can study it:
def featureClassName(table_path, fc):
'''Will update the FEATURECLASS FIELD in table_path rows with
value of fc (string) where FEATURECLASS field is currently null '''
arcpy.AddMessage("Calculating Feature Class Name...")
print "Calculating Feature Class Name..."
#delimit field correctly for the query expression
df = arcpy.AddFieldDelimiters(fc, 'FEATURECLASS')
ex = df + " is NULL"
flds = ['FEATURECLASS']
#in case we don't get rows, del will bomb below unless we put in a ref
#to row
row = None
#do the work
with arcpy.da.UpdateCursor(table_path, flds, ex) as cursor:
for row in cursor:
row[0] = fc #or basename, don't know which you want
cursor.updateRow(row)
del cursor, row
Notice we are now passing the name of the fc as an arg, so you will have to deal with that in the rest of your code. Also it's best to use AddFieldDelimiter, since different fc's require different delimiters, and the docs are not clear at all on this (sometimes they are just wrong).
good luck, Mike