Django-herald: Can't send mails with attachments - python

Anyone using Django herald for sending notifications?
I've been struggling for days to make it work but the lack of documentation and silent failures made it impossible to debug the issues. It seems that the mails are not being sent if I include an attachment in it.
from herald.base import EmailNotification
def sendMail():
SendThisMail(user, my_modal).send(user=my_user) # creates an error on this line as the file object is closed and inaccessible.
#registry.register_decorator()
class SendThisMail(SomeBaseClass, EmailNotification):
def __init__(self, user, my_modal: models.MyModal):
super().__init__(user, my_modal)
self.subject = "abc"
file = open('.staticfiles/assets/some.pdf', 'rb')
self.attachments = [('attachment_1', File(file))]
self.context = {
**self.context,
'subject': self.subject,
'attachment': self.attachments,
}
self.to_emails = [user.email]
What's wrong with it?

From the project docs:
Each attachment in the list can be one of the following:
A tuple which consists of the filename, the raw attachment data, and the mimetype. It is up to you to get the attachment data
So the relevant parts of your code code should be something like:
data = open('.staticfiles/assets/some.pdf', 'rb').read()
self.attachments = [('attachment_1', data, 'application/pdf')]

Related

Sending message with attachment in email BytesIO doesn't work - No errors shown - Python/Smtp/Email

I am trying to add attachment to the object created below and stored as email. It's all done within a class and this is how the class is initialised:
class <name>:
def __init__(self, **kwargs):
self.msg = kwargs.get("msg", MIMEMultipart())
self.subject = kwargs.get("subject", None)
self.body = kwargs.get("body", None)
self.attachment = kwargs.get("attachment", None)
Attachment is not being sent
The attachments works fine as long as the attachment is of the type str. Add other functionalities of adding body, subject, etc. works fine.
When I pass a BytesIO() type object, the mail gets sent without any errors or warnings, but the attachment is not being sent.
for i ,attachment in enumerate(self.attachment,start=1):
p = MIMEBase('application', 'octet-stream')
if isinstance(attachment, str):
p.set_payload(open(attachment,"rb").read())
else:
p.set_payload((attachment).read())
encoders.encode_base64(p)
p.add_header('Content-Disposition', "attachment", filename= f'attachment{i}.jpg')
self.msg.attach(p)
Why am I using BytesIO() in the first place?
Well, I am trying to send a QR Image, which is saved using
qr_img.save(BytesIO(),format="png")
I am trying to send the image generated from qrcode module without saving it locally.
Message is sent like this:
self.server.send_message(msg)
Where self.server is the smtplib.SMTP_SSL() object.
Any help is appreciated, thanks!
The issue was that the buffer values were not read using .read() method. Using the .getvalue() method instead worked for me.
Note: No errors are thrown when using .read().

Django don't send email message with attached zip file

everyone! I have a problem with sending an email with Django smtp email backend and SendGrid. My view is below.
It makes batch of pdf files with pyppeteer, zip them to archive, attaches to the message and then send it to the email. It should be, but it doesn't work!
I checked it with other view for email sending(a little bit diff logic without zip or something.) and it worked but this means that it's not the problem with sendgrid and any other stuff like secret keys in uwsgi configuration.
The fact is, emails don't stuck in send grid list.
I tried to put some dummy bites instead of pdf file like pdf_file = b'some bytes' and it worked on my local machine but didn't work on server. I got the size of zip-archive from a response. Please explain to me where I'm wrong.
### views.py
#csrf_exempt
#require_POST
def generate_batch_pdf(request, blueprint):
try:
payload = json.loads(request.body)
email = payload.get('email')
uuids = payload.get('uuids')
zip_buffer = io.BytesIO()
compression = zipfile.ZIP_DEFLATED
with zipfile.ZipFile(zip_buffer, 'a', compression, False) as f:
for uuid in uuids:
report_name = _report_name(uuid, blueprint)
pdf_file = async_to_sync(generate_pdf_file)(
request.get_host(),
uuid,
blueprint,
report_name,
)
f.writestr(
zinfo_or_arcname='{0}'.format(report_name),
data=pdf_file,
)
msg = EmailMessage(
subject='Отчеты Тест',
body='Архив выбранных отчетов',
from_email=settings.DEFAULT_FROM_EMAIL,
to=[email],
)
msg.attach('reports.zip', zip_buffer.getvalue(), 'application/zip')
msg.send(fail_silently=False)
return JsonResponse(
{
'msg': 'Ok',
# it's here just for check
'size': '{0}'.format(sys.getsizeof(zip_buffer)),
},
)
except Exception as e:
return JsonResponse({'msg': 'Err -> {e}'.format(e=e)})
I'm using python 3.6
### requirements.txt
django==3.1
...

Upload multiple files using simple-salesforce python

I started learning SalesForce and developing apps using django.
I need assistance with uploading a file to salesforce, For that I read simple-salesforce and this that help to upload file using rest and SOAP api.
My question is how do I upload one or more files using simple-salesforce?
Here is the code block I use for uploading files.
def load_attachments(sf, new_attachments):
'''
Method to attach the Template from the Parent Case to each of the children.
#param: new_attachments the dictionary of child cases to the file name of the template
'''
url = "https://" + sf.get_forced_url() + ".my.salesforce.com/services/data/v29.0/sobjects/Attachment/"
bearer = "Bearer " + sf.get_session_id()
header = {'Content-Type': 'application/json', 'Authorization': bearer}
for each in new_attachments:
body = ""
long_name = str(new_attachments[each]).split(sep="\\")
short_name = long_name[len(long_name) - 1]
with open(new_attachments[each], "rb") as upload:
body = base64.b64encode(upload.read())
data = json.dumps({
'ParentId': each,
'Name': short_name,
'body': body
})
response = requests.post(url, headers=header, data=data)
print(response.text)
Basically, to send the file, you need to use the requests module and submit the file via a post transaction. The post transaction requires the URL to which the request is sent, the header information, and the data.
Here, sf is the instance of returned by the simple-salesforce initialization. Since my instance uses custom domains, I had to create my own function in simple-salesforce to handle that; I call it get_forced_url(). Note: The URL is may be different for you depending on which version you are using [the v29.0 portion may change].
Then I set up my bearer and header.
The next thing is a loop that submits a new attachment for each attachment in a map from Parent ID to the File I wish to upload. This is important to note, attachments must have a Parent Object so you need to know the ParentId. For each attachment, I blank out the body, create a long and short name for the attachment. Then the important part. On attachments, the actual data of the file is stored as a base-64 binary array. So the file must be opened as binary, hence the "rb" and then encoded to base-64.
Once the file has been parsed to base-64 binary, I build my json string where ParentId is the object ID of the parent object, the Name is the short name, and the body is the base-64 encoded string of data.
Then the file is submitted to the URL with the headers and data. Then I print the response so I could watch it happening.
To upload files, you only need simple-salesforce
Complete example, including creating Account, Contact and Case. Then attaching the file to Case.
#Create Account, Contact and Case
AccountID = sf.Account.create({'Name':'Test12','Phone':'987654321'})["id"]
ContactID = sf.Contact.create({'LastName':'Smith2','Email':'example3#example.com'})["id"]
CaseID = sf.Case.create({'AccountId':AccountID,'ContactId':ContactID,'Description':'Test4321','Subject':'Test4321'})
#Convert image to Base64
import json, base64
with open('test1.png', mode='rb') as file:
img = file.read()
image = base64.encodebytes(img).decode('utf-8')
#The simple example
sf.Attachment.create({'ParentId': CaseID["id"],'Name':'TestFile1','body': image,'ContentType':'image/png'})
And how to change the 'one-file' example to multiple files
sf.bulk.Attachment.insert([
{'ParentId': CaseID["id"],'Name':'TestFile2','body': image,'ContentType':'image/png'},
{'ParentId': CaseID["id"],'Name':'TestFile3','body': image,'ContentType':'image/png'},
{'ParentId': CaseID["id"],'Name':'TestFile4','body': image,'ContentType':'image/png'},
{'ParentId': CaseID["id"],'Name':'TestFile5','body': image,'ContentType':'image/png'},
{'ParentId': CaseID["id"],'Name':'TestFile6','body': image,'ContentType':'image/png'},
{'ParentId': CaseID["id"],'Name':'TestFile7','body': image,'ContentType':'image/png'},
{'ParentId': CaseID["id"],'Name':'TestFile8','body': image,'ContentType':'image/png'},
{'ParentId': CaseID["id"],'Name':'TestFile9','body': image,'ContentType':'image/png'}],batch_size=1000,use_serial=True)
(you know how to fix the rest)

Deleting dataset attachment in Socrata with API

I'm working on a python script that will:
1) pull GIS metadata from an enterprise database
2) parse the metadata from XML to plain text
3) attach the text files to the corresponding published datasets in Socrata (which are published monthly)
4) The script will also be run monthly, so that any schema changes in the enterprise dataset are reflected in the attached plain text metadata files on Socrata.
I've been able to successfully attach the text metadata files to published Socrata datasets using some code found here. The problem is, each time the script is run, an additional attachment is added. I would like to either delete the existing attachment and add a new one, or overwrite the existing attachment with the contents of the new one.
I've done a fair amount of research on this, and can't seem to find any documentation for managing attachments using the Socrata API. Any suggestions?
I ended up figuring this one out. Had to alter a few lines to empty out the attachments in the attach_file function in the Socrata Python library from:
def attach_file(self, filename):
metadata = self.metadata()
if not metadata.has_key('attachments'):
metadata['attachments'] = []
to:
def attach_file(self, filename):
metadata = self.metadata()
metadata['attachments'] = [] #empty out metadata, regardless of existing metadata
Ended up using the same API and was able to replace attachments using the following code:
def attach_file(self, filename, clear_metadata):
metadata = self.metadata()
if not metadata.has_key('attachments'):
metadata['attachments'] = []
# if the user wants to clear all existing attachments on dataset
if clear_metadata:
metadata['attachments'] = []
response = self.multipart_post('/assets', filename)
if not response.has_key('id'):
print "Error uploading file to assets service: no ID returned: %s" % response
return
attachment = {'blobId': response['id'],
'name': response['nameForOutput'],
'filename': response['nameForOutput']}
metadata['attachments'].append(attachment)
self._request("/views/%s.json" % self.id, 'PUT', {'metadata':metadata})
def multipart_post(self, url, filename, field='file'):
print("Running multipart_post")
authBase64 = base64.encodestring('%s:%s' % (self.username, self.password)).replace('\n', '')
datagen, headers = multipart_encode({field: open(filename, "rb")})
headers['X-App-Token'] = self.app_token
headers['Authorization'] = "Basic %s" % authBase64
print("url=" + url)
request = Request("%s%s" % (self.url, url), datagen, headers)
print(str(Request))
response = urlopen(request).read()
return json.loads(response)

JIRA Python add_attachment() 405 Method Not Allowed

I am trying to upload a file to JIRA via its REST API using the python lib found here: jira python documentation
It seems pretty straight forward I wrote a method that allows me to pass an issue and then it attaches a filename. and one that lets me retrieve an issue from JIRA.
from jira.client import JIRA
class JIRAReport (object):
def attach(self,issue):
print 'Attaching... '
attachment = self.jira.add_attachment(issue, attachment=self.reportpath, filename='Report.xlsx')
print 'Success!'
def getissue(self):
if not self.issue == None:
return self.jira.issue(self.issue)
return None
then in my main script I am getting the issue and attaching the file to an issue I retrieved from JIRA
report = JiraReport()
report.issue = 'ProjectKey-1'
report.reportpath = '../report_upload/tmp/' + filename
issue = report.getissue()
if not issue == None:
report.attach(issue)
else:
print "No Issue with Key Found"
I am able to get the issue/create issues if needed but when using the self.jira.add_attachment() method I am getting 405 Method Not Allowed.
The file exists and is able to be opened.
Here is the add_attachment() method from the source code:
def add_attachment(self, issue, attachment, filename=None):
"""
Attach an attachment to an issue and returns a Resource for it.
The client will *not* attempt to open or validate the attachment; it expects a file-like object to be ready
for its use. The user is still responsible for tidying up (e.g., closing the file, killing the socket, etc.)
:param issue: the issue to attach the attachment to
:param attachment: file-like object to attach to the issue, also works if it is a string with the filename.
:param filename: optional name for the attached file. If omitted, the file object's ``name`` attribute
is used. If you aquired the file-like object by any other method than ``open()``, make sure
that a name is specified in one way or the other.
:rtype: an Attachment Resource
"""
if isinstance(attachment, string_types):
attachment = open(attachment, "rb")
# TODO: Support attaching multiple files at once?
url = self._get_url('issue/' + str(issue) + '/attachments')
fname = filename
if not fname:
fname = os.path.basename(attachment.name)
content_type = mimetypes.guess_type(fname)[0]
if not content_type:
content_type = 'application/octet-stream'
files = {
'file': (fname, attachment, content_type)
}
r = self._session.post(url, files=files, headers=self._options['headers'])
raise_on_error(r)
attachment = Attachment(self._options, self._session, json.loads(r.text)[0])
return attachment
It is mentioned in documentation that as a argument they expect file-like object.
Try to do something like :
file_obj = open('test.txt','rb')
jira.add_attachment(issue,file_obj,'test.txt')
file_obj.close()
Check that the URL that you are specifying for JIRA (if using the on-demand service) is https://instance.atlassian.net.
I just hit this as well, and it sends a POST request to http://instance.atlassian.net and gets redirected to https://instance.atlassian.net, but the client sends a GET request to the redirected address (see: https://softwareengineering.stackexchange.com/questions/99894/why-doesnt-http-have-post-redirect for more information)

Categories

Resources