odoo-12 : How to solve the write() method problem - python

In my case i inherited the crm form and add one mamy2many field this name [Estimation Assign to] when i select the users from this field and save the record that time selected users are added in Add Followers and also send the mail. Now problem is that when i change the kanban card from one stage to another stage that time if in form estimation field assigned some users the mail are also send those users.
but i want to send mail only when i'm open the record and select the users from estimation field and then click on save button only that i want to sand mail. not when i change the kanban card from one stage to another stage.                                                         
if you know it please let me know.
#api.model
def create(self, vals):
lead_res = super(CrmLead, self).create(vals)
for rec in lead_res:
if rec.estimation_id:
partner_ids = []
for est_rec in rec.estimation_id:
if est_rec.partner_id and est_rec.partner_id.email:
user_name = self.env.user.name_get()[0][1]
partner_ids.append(est_rec.partner_id.id)
template_obj = self.env['mail.mail']
template_data = {
'subject': 'New Estimation Asign : ',
'body_html': "Hello,</br><h5>" + user_name + " invited you to follow Lead/Opportunity document : " + rec.name + "</h5>",
'email_from': self.env['mail.message']._get_default_from(),
'email_to': est_rec.partner_id.email
}
template_id = template_obj.create(template_data)
template_obj.send(template_id)
if partner_ids:
rec.message_subscribe(partner_ids, None)
return lead_res
#api.multi
def write(self, vals):
res = super(CrmLead, self).write(vals)
for rec in self:
if rec.estimation_id:
partner_ids = []
for est_rec in rec.estimation_id:
if est_rec.partner_id and est_rec.partner_id.email:
user_name = self.env.user.name_get()[0][1]
partner_ids.append(est_rec.partner_id.id)
template_obj = self.env['mail.mail']
template_data = {
'subject': 'New Estimation Asign : ',
'body_html': "Hello,</br><h5>" + user_name + " invited you to follow Lead/Opportunity document : " + rec.name + "</h5>",
'email_from': self.env['mail.message']._get_default_from(),
'email_to': est_rec.partner_id.email
}
template_id = template_obj.create(template_data)
print('===================To sent ==================', est_rec.partner_id.email)
template_obj.send(template_id)
rec.message_subscribe(partner_ids, None)
#message_unsubscribe
message_partner_ids = rec.message_partner_ids.ids
est_ids = [est_rec.partner_id.id for est_rec in rec.estimation_id] + [self.env.ref('base.partner_root').id]
unsub_partners = set(message_partner_ids) - set(est_ids)
if list(unsub_partners):
rec.message_unsubscribe(list(unsub_partners))
else:
print("+++++============= Else Part =============+++++")
return res

Try to add another condition to send mails when estimation_id has changed.
if u'estimation_id' in vals and rec.estimation_id:
EDIT
The following code will compute the new added users:
user_ids = {rec.id: [user_id.id for user_id in rec.estimation_id] for rec in self}
res = super(CrmLead, self).write(vals)
for rec in self:
new_user_ids = [user.id for user in rec.estimation_id if user.id not in user_ids[rec.id]]

Related

Python telegram bot, how to store and show users data for each users?

Hello I am new one for python and telegram api so i have some questions. I am creating telegram bot(python telegram api) with users profile. I have created database(mysql.connector) and store there all users info after registration. Also i have created user class. When user types /start i am checking if it exists, if it is, i am filling this class. Then i use this class to show some profile information(photo, name, age, etc.) if users click on button(my profile). So the problem is when i have 2-users at the same time. First typed "/start" and logged in, wanna watch self profile, everything fine. But when second user do the same, i got that the first users when clicked on (my profile), he or she got the last one profile who typed "/start" loaded for both users. How to fix this? Solution to check and load data all the time sounds not good, i'd like to do smth with "class Users", but i don't know to make it uniq for each users session. Any solutions? If it's needed i can give more code, just ask.
class Users:
def __init__(self, id=0, name='', age=0, gender='', balance=0, telegram_id=0, photo='', sallarytext=0, sallaryvideo=0, videocall=0):
self.id = id
self.name = name
self.age = age
self.gender = gender
self.balance = balance
self.telegram_id = telegram_id
self.photo = photo
self.sallarytext = sallarytext
self.sallaryvideo = sallaryvideo
self.videocall = videocall
user = Users()
def check_auth(connection, telegram_id):
cursor = connection.cursor()
result = None
try:
cursor.execute("SELECT * FROM users WHERE telegram_id = '%s'" % telegram_id)
result = cursor.fetchall()
data = []
if result:
for row in result:
user.id = row[0]
user.name = row[1]
user.age = row[2]
user.gender = row[3]
user.telegram_id = row[4]
user.balance = row[5]
data = [user.name]
if user.gender == 'Female':
cursor.execute("SELECT * FROM photos WHERE users_id = '%s'" % user.id)
result2 = cursor.fetchall()
for row in result2:
user.photo = row[1]
user.sallarytext = row[2]
user.sallaryvideo = row[3]
user.videocall = row[4]
return data
except Error as e:
print(f"The error '{e}' occurred")
#bot.message_handler(commands=['start'])
def check_reg(message):
if message.chat.type == 'private':
telegram_id = message.from_user.id
# create_db_users(connection)
# create_db_photos(connection)
# create_db_chats(connection)
data_user = check_auth(connection, telegram_id)
if not data_user:
new_user(message) # user registration
else:
if user.gender == 'Male':
default_user_keybord(message) # show user keybord
elif user.gender == 'Female':
default_model_keybord(message)
def show_profile(message): # funtion show profile when user click on "My profile" button
profile_text = "Profile\n\nYour name: " + user.name + "\nYour age: " + str(
user.age)
menu_keybord = types.ReplyKeyboardMarkup(row_width=2, resize_keyboard=True)
button_name_age = types.KeyboardButton(text="🗣 Change name/age")
button_back = types.KeyboardButton(text="◀️ Return")
menu_keybord.add(button_name_age, button_back)
bot.send_message(message.chat.id, profile_text, reply_markup=menu_keybord)
Could you tell me what telegram api package you are using exactly?
The core of your problem, I think, is the use of a global variable user to store user data. It would be best practice to instantiate and return a new Users every time you call check_auth.
That being said,
in Python, if you want to update a global variable, say user, you have to use the statement global user before you do so;
consider using an ORM such as SQLAlchemy to spare you some headaches and code.
Let me know if that solved your issue.
C
I have fixed this by doing request to the database, getting data and pushing it to class, then closing connection with database and showing to user

TypeError: must be str, not mail.mass_mailing.list

I am making a module on Odoo to send sms from a mailing list.
I have run this code but I have an error.
contact_list_ids = fields.Many2many('mail.mass_mailing.list', 'phone_number', string='Liste de diffusion')
messages_sms = fields.Text(string="message", required=True)
# send SMS with GET method
#api.multi
def send_sms(self):
for list in self.contact_list_ids:
for contact in list:
final_url = (
URL +
'&to=' +contact +
'&sms=' + self.messages_sms
)
r = requests.get(final_url)
if not r:
return ERROR_API
return r.text
The error:
File "c:\program files (x86)\odoo 12.0\server\addons\KeoMarketing\models\messages_sms.py", line 36, in send_sms
'&sms=' + self.messages_sms
TypeError: must be str, not mail.mass_mailing.list
You got a type error because you can't concatenate str with a mail.mass_mailing.list record.
'&to=' + contact
Try to pass the phone number instead (or any field used to store the phone number):
'&to=' + contact.phone_number
You can check the following Twilio example:
from twilio.rest import Client
class CustomClass(models.Model):
contact_list_ids = fields.Many2many('mail.mass_mailing.list', 'phone_number', string='Liste de diffusion')
messages_sms = fields.Text(string="message", required=True)
#api.multi
def send_sms(self):
self.ensure_one()
account_sid = ''
auth_token = ''
client = Client(account_sid, auth_token)
for list in self.contact_list_ids:
for contact in list.contact_ids:
client.messages \
.create(
body=self.messages_sms,
from_=self.env.user.partner_id.mobile,
status_callback='http://postb.in/1234abcd',
to=contact.phone_number
)
class MailMassMailingContact(models.Model):
_inherit = 'mail.mass_mailing.contact'
phone_number = fields.Char()
The phone number used to send SMS is stored in the related partner of the current user
I inherited mail.mass_mailing.contact to add a phone number for each contact

How to display image on web page based on selected value from select field without reloading page

I am trying to figure out how to display an image on the webform page when a certain option is selected from the select field. And then getting the image to change when a different option is selected. The url image link is stored in the Sqlite3 database with the respective select field options. I am having trouble putting this all together and was wondering if someone may be able to help. Here is some code that may make things easier to understand.
The select field I am focusing on is the Host field and the extra part of the Host class is where the URL image link is stored.
class LoginForm(FlaskForm):
host = SelectField('Host Solution Required', choices = [], validators= .
[validators.InputRequired('Please select a Board')])
#app.route('/', methods=['GET', 'POST'])
def form():
form = LoginForm()
form.device.choices = [(device.id, device.name) for device in
Device.query.filter_by(cat=None).all()]
form.host.choices= [(host.id, host.name) for host in Host.query.all()]
#if form.validate_on_submit(): To use this I will have to figure out how
to validate the choices for device
if request.method =='POST':
device = Device.query.filter_by(id=form.device.data).first()
host = Host.query.filter_by(id= form.host.data).first()
#This code takes care of changing the format of the ordering link
that is put into bugzilla.
if host.name =='None' and form.cat.data == 'None':
return '<h1> You did not order anything, please go back and fill
out cat or host</h2>'
elif host.name == 'None':
return '<h1> ({} x {}) for {}</h1>'.format(form.number.data,
device.name, form.team.data)
elif form.cat.data == 'None':
return '<h1> ({} x {}-{}) for {} .
</h1>'.format(form.quantity.data, host.name, form.finish.data,
form.team.data)
else:
return '<h1> ({} x {}-{}) AND ({} x {}) for {} .
</h1>'.format(form.quantity.data, host.name, form.finish.data,
form.number.data, device.name, form.team.data)
return render_template('form.html', form=form, street = host.extra)
class Host(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
customer = db.Column(db.Boolean())
engineering = db.Column(db.Boolean())
extra = db.Column(db.String(400))
#app.route('/host/<name>')
def host(name):
hosts = Host.query.filter_by(name = name).all()
hostArray = []
for host in hosts:
hostObj = {}
hostObj['id'] = host.id
hostObj['name'] = host.name
hostObj['extra'] = host.extra
hostArray.append(hostObj)
return jsonify({'hosts':hostArray})
<br>
{{form.host.label}}
{{form.host}}
<img src={{street}} alt = 'pic' align = 'right' width = '200' height =
'200' />
<script>
var host_select = document.getElementById("host");
host_select.onchange = function() {
name = host_select.extra;
fetch('/host/' + name).then(function(response)){
response.json().then(function(data)){
for(host of data.hosts){
document.getElementById("tweet").src= host.extra;
}
})
});
}*/
</script>
The snippet above does not work, but it is where the image is being shown in the HTML.
Please let me know if I can clarify anything further.

Django Rest Framework: invalidating a single part of a multi serialized POST

Working with django-rest-framework I'm using a serializer with many=True, checking for items which already exist and invalidating them.
The problem is:
When part of a request is invalid, the whole request is rejected without creating the valid objects.
Sample Payload:
[{'record_timestamp': '2016-03-04T09:46:04', 'reader_serial': u'00000000f9b320ac', 'card_serial': u'048EC71A0F3382', 'gps_latitude': None, 'gps_longitude': None, 'salt': 34, 'reader_record_id': 1063},
{'record_timestamp': '2016-03-04T09:46:06', 'reader_serial': u'00000000f9b320ac', 'card_serial': u'04614B1A0F3382', 'gps_latitude': None, 'gps_longitude': None, 'salt': 34, 'reader_record_id': 1064}]
Sample response:
[{"last_record_id":[2384],"error":["This record already exists"]},{}]
Ideal response:
[{"last_record_id":[2384],"error":["This record already exists"]},{'reader': 10, 'card': 12, 'gps_latitude': None, 'gps_longitude': None, 'reader_record_id': 1064}}]
I'd like the first record to provide the error, but the second record to be correctly created, with the response being the object created.
class CardRecordInputSerializer(serializers.ModelSerializer):
class Meta:
model = CardRecord
fields = ('card', 'reader', 'bus', 'park', 'company', 'client',
'record_timestamp', 'reader_record_id')
read_only_fields = ('card', 'reader', 'bus', 'park', 'company'
'client')
def validate(self, data):
"""
Check that the record is unique
"""
#import ipdb; ipdb.set_trace()
hash_value = data.get("hash_value", None)
if CardRecord.objects.filter(hash_value=hash_value):
raise ValidationError(
detail={"error":"This record already exists",
"last_record_id":data.get("reader_record_id", None)})
else:
return data
def to_internal_value(self, data):
internal_value = super(CardRecordInputSerializer, self)\
.to_internal_value(data)
card_serial = data.get("card_serial", None).upper()
reader_serial = data.get('reader_serial', None).upper()
record_timestamp = data.get('record_timestamp', None)
date_altered = False
record_date = dateutil.parser.parse(record_timestamp)
#check if clock has reset to 1970
if record_date < datetime.datetime(2014, 4, 24):
record_date = datetime.datetime.now().isoformat()
date_altered = True
#create a hash to check that this record is unique
salt = data.get('salt', None)
hash_generator = hashlib.sha1()
hash_generator.update(card_serial)
hash_generator.update(reader_serial)
hash_generator.update(str(record_timestamp))
hash_generator.update(str(salt))
hash_value = str(hash_generator.hexdigest())
internal_value.update({
"card_serial": card_serial,
"reader_serial": reader_serial,
"salt": salt,
"hash_value": hash_value,
"record_timestamp": record_date,
"date_altered": date_altered
})
return internal_value
def create(self, validated_data):
#import ipdb; ipdb.set_trace()
'''
Create a new card transaction record
'''
try:
card_serial = validated_data.get('card_serial', None)
card = Card.objects.filter(uid=card_serial).last()
reader_serial = validated_data.get('reader_serial', None)
reader = Reader.objects.filter(serial=reader_serial).last()
#if we havent seen this reader before, add it to the list
if not reader:
reader = Reader.objects.create(serial=reader_serial)
company = card.company
client = reader.client
park = reader.park
record_timestamp = validated_data.get('record_timestamp', None)
reader_record_id = validated_data.get('reader_record_id', None)
#if datetime is naive, set it to utc
if record_timestamp.tzinfo is None \
or record_timestamp.tzinfo.utcoffset(d) is None:
record_timestamp = pytz.utc.localize(record_timestamp)
hash_value = validated_data.get('hash_value', None)
date_altered = validated_data.get('date_altered', None)
return CardRecord.objects.create(card = card,
reader = reader,
company = company,
client = client,
park = park,
record_timestamp = record_timestamp,
reader_record_id = reader_record_id,
hash_value = hash_value,
date_altered = date_altered)
#Usually a card that doesn't have company
except AttributeError:
return {
'status': 'Bad Request',
'message': 'One of the values was malformed or does not exist.'
}
How can I create valid objects and provide errors for the invalid ones?
I ended up skipping the validation.
Then in my create method if the object already exists I just return it, if it doesn't exist I create it and return it.
The client no longer knows it the server had that record, but thats fine for my use case.
I also swapped to using PUT to reflect the fact that the method is idempotent.
I feel like the validator is the place to do the check but this works.
class CardRecordInputSerializer(serializers.ModelSerializer):
class Meta:
model = CardRecord
fields = ('card', 'reader', 'bus', 'park', 'company', 'client',
'record_timestamp', 'reader_record_id')
read_only_fields = ('card', 'reader', 'bus', 'park', 'company'
'client')
def validate(self, data):
"""
Check that the record is unique
"""
#import ipdb; ipdb.set_trace()
#<--------Removed the validation
return data
def to_internal_value(self, data):
internal_value = super(CardRecordInputSerializer, self)\
.to_internal_value(data)
card_serial = data.get("card_serial", None).upper()
reader_serial = data.get('reader_serial', None).upper()
record_timestamp = data.get('record_timestamp', None)
date_altered = False
record_date = dateutil.parser.parse(record_timestamp)
#check if clock has reset to 1970
if record_date < datetime.datetime(2014, 4, 24):
record_date = datetime.datetime.now().isoformat()
date_altered = True
#create a hash to check that this record is unique
salt = data.get('salt', None)
hash_generator = hashlib.sha1()
hash_generator.update(card_serial)
hash_generator.update(reader_serial)
hash_generator.update(str(record_timestamp))
hash_generator.update(str(salt))
hash_value = str(hash_generator.hexdigest())
internal_value.update({
"card_serial": card_serial,
"reader_serial": reader_serial,
"salt": salt,
"hash_value": hash_value,
"record_timestamp": record_date,
"date_altered": date_altered
})
return internal_value
def create(self, validated_data):
#import ipdb; ipdb.set_trace()
'''
Create a new card transaction record
'''
try:
card_serial = validated_data.get('card_serial', None)
card = Card.objects.filter(uid=card_serial).last()
reader_serial = validated_data.get('reader_serial', None)
reader = Reader.objects.filter(serial=reader_serial).last()
#if we havent seen this reader before, add it to the list
if not reader:
reader = Reader.objects.create(serial=reader_serial)
company = card.company
client = reader.client
park = reader.park
record_timestamp = validated_data.get('record_timestamp', None)
reader_record_id = validated_data.get('reader_record_id', None)
#if datetime is naive, set it to utc
if record_timestamp.tzinfo is None \
or record_timestamp.tzinfo.utcoffset(d) is None:
record_timestamp = pytz.utc.localize(record_timestamp)
hash_value = validated_data.get('hash_value', None)
date_altered = validated_data.get('date_altered', None)
record = CardRecord.objects.filter(hash_value=hash_value).last()
if record: #<--------Check if that object already exists
return record #<-------- if it does just return it
else: #<-------- otherwise make it
return CardRecord.objects.create(
card = card,
reader = reader,
company = company,
client = client,
park = park,
record_timestamp = record_timestamp,
reader_record_id = reader_record_id,
hash_value = hash_value,
date_altered = date_altered)
#Usually a card that doesn't have company
except AttributeError:
return {
'status': 'Bad Request',
'message': 'One of the values was malformed or does not exist.'
}

Uploading files in Google App Engine. How to get the file to the upload Handler Class

I have a form in google app engine where I want to upload an image and all my text at the same time. Do I have to seperate this into two seperate pages and actions?
Here is my upload handler:
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def upload(self, reseller_id, imgfile):
upload_files = imgfile
blob_info = upload_files[0]
key = blob_info.key()
r = Reseller.get_by_id(reseller_id)
r.blob_key_logo = str(key)
r.put();
Here is my creation of a new reseller object:
class NewReseller(BaseHandler):
def get(self):
if self.user:
self.render("new_reseller.html")
else:
self.redirect("/display_resellers")
def post(self):
name = self.request.get('name')
website = self.request.get('website')
information = self.request.get('information')
address = self.request.get('address')
city = self.request.get('city')
state = self.request.get('state')
zipcode = self.request.get('zipcode')
email = self.request.get('email')
phone = self.request.get('phone')
r = Reseller( name = name,
website = website,
information = information,
address = address,
city = city,
state = state,
zipcode = zipcode,
email = email,
phone = phone)
r.put()
theresellerid = r.key().id()
#And then Upload the image
u = UploadHandler()
logo_img = u.get_uploads('logo_img')
u.upload(theid, logo_img)
self.redirect('/display_resellers')
I think my problem here is this line:
logo_img = u.get_uploads('logo_img')
it pops out the error message
for key, value in self.request.params.items():
AttributeError: 'NoneType' object has no attribute 'params'
Somehow I need this NewReseller class to inherit the .getuploads from BlobstoreUploadHandler so I can do:
logo_img = self.get_uploads('logo_img')
Or there is probably a better way because this seems a little messy.
So my question is how to upload files and data in one form on just one page. I could do it with two seperate pages. One for adding the reseller and one for adding the image but that seems over complicated.
I tried to follow some steps and clues from this question:
Upload files in Google App Engine
******Edit***** Working Implementation Below:
class EditReseller(BaseHandler, blobstore_handlers.BlobstoreUploadHandler):
def get(self, reseller_id):
if self.user:
reseller = Reseller.get_by_id(int(reseller_id))
upload_url = blobstore.create_upload_url('/upload')
image = True
if reseller.blob_key_logo is None:
image = False
self.render('edit_reseller.html', r=reseller, reseller_id=reseller_id, upload_url=upload_url, image=image)
else:
self.redirect('/admin')
class UploadHandler(blobstore_handlers.BlobstoreUploadHandler):
def post(self):
reseller_id = self.request.get('reseller_id')
upload_files = self.get_uploads('logo_img')
if upload_files:
blob_info = upload_files[0]
key = blob_info.key()
r = Reseller.get_by_id(int(reseller_id))
r.blob_key_logo = str(key)
r.put();
name = self.request.get('name')
website = self.request.get('website')
information = self.request.get('information')
address = self.request.get('address')
city = self.request.get('city')
state = self.request.get('state')
zipcode = self.request.get('zipcode')
email = self.request.get('email')
phone = self.request.get('phone')
if name and website and information and email and phone and address and city and state and zipcode:
r = Reseller.get_by_id(int(reseller_id))
r.name = name
r.website = website
r.information = information
r.address = address
r.city = city
r.state = state
r.zipcode = zipcode
r.email = email
r.phone = phone
r.put()
else:
error = "Looks like your missing some critical info"
self.render("edit_reseller.html", name=name, website=website, information=information, address=address, city=city, zipcode=zipcode, email=email, phone=phone, error=error)
self.redirect("/edit_reseller/" + reseller_id)
You just need to put the logic of the UploadHandler inside the Reseller(BaseHandler) and make Reseller inherit from blobstore_handlers.BlobstoreUploadHandler.
The call to get_uploads fails, as the NewReseller Class does not inherit from BlobstoreUploadHandler. The BlobstoreUploadHandler class takes over the upload operation so you do not need to create a post method, just add the corresponding logic from post ( name = self.request.get('name'), r = Reseller(), r.put(), etc. ) and add it to the upload method.
You should not call or create a new a handler instance by hand (unless you know what you are doing), as it would be missing the things that make it work.
The complete app sample at the official docs, might also be helpful.

Categories