We have some customers who uses Microsoft Outlook to send attachments. However in odoo we see only winmail.dat files (while everything looks ok in mail client).
Is there any way to force odoo to expose winmail.dat content?
The problem is that Microsoft Outlook uses Transport Neutral Encapsulation Format and packs all attachments in one file.
There is a good python parser for tnef format - tnefparse. I'd suggest you to use it and write simple module to extend mail.thread model like this
from tnefparse import TNEF
from openerp.osv import osv
class MailThread(osv.Model):
_inherit = 'mail.thread'
def _message_extract_payload(self, message, save_original=False):
body, attachments = \
super(MailThread, self)\
._message_extract_payload(message, save_original=save_original)
new_attachments = []
for name, content in attachments:
new_attachments.append((name, content))
if name and name.strip().lower() in ['winmail.dat', 'win.dat']:
try:
winmail = TNEF(content)
for attach in winmail.attachments:
new_attachments.append((attach.name, attach.data))
except:
# some processing here
pass
return body, new_attachments
You can find more information on how to do custom modules here.
Related
I'm trying to search "All Outlook Items" and then find emails based on the subject list I input into the code. Once the email is found, it is moved to another folder and marked as "Task Complete" (The green check in the emails).
However, I'm having a couple of errors when trying to run the code. If anyone can guide me it'd be amazing.
Here's the code:
import win32com.client
Email = 'johndoe#gmail.com'
subjects = input("Enter a list of subjects separated by commas: ").split(",")
MoveToFolder = "folder1"
Iter_Folder = "folder2"
def find_and_download_case_number_related_emails():
Outlook = win32com.client.Dispatch("Outlook.Application")
Outlook_Location = Outlook.GetNamespace("MAPI")
Lookin_Folder = Outlook_Location.Folders[Email].Folders[Iter_Folder]
Out_MoveToFolder = Outlook_Location.Folders[Email].Folders[MoveToFolder]
for message in Lookin_Folder:
if message.TaskCompleted:
continue
for message in Lookin_Folder:
if message.Subject in subjects:
message.Move(Out_MoveToFolder)
for message in Out_MoveToFolder:
message.MarkAsTaskCompleted()
if __name__ == "__main__":
find_and_download_case_number_related_emails()
and here's the error I'm getting at the moment:
raise AttributeError("%s.%s" % (self._username_, attr))
AttributeError: <unknown>.Items. Did you mean: 'Item'?
The following line of code contains a wrong property call:
outlook.Folders.Items.Restrict
The Folders class doesn't provide the Items property. You need to get a Folder instance and only then use Items property.
I'd suggest using the NameSpace.GetDefaultFolder method which returns a Folder object that represents the default folder of the requested type for the current profile; for example, obtains the default Inbox folder for the user who is currently logged on.
To understand how the Restrict or Find/FindNext methods work in Outlook you may take a look at the following articles that I wrote for the technical blog:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
Is the google app engine document not updated?
It works fine (send email with attachment), when I do this:
message = mail.EmailMessage( sender=EMAIL_SENDER,
subject=subject,body=theBody,to=['test#gmail.com'],attachments=[(attachname,
new_blob.archivoBlob)])
message.send()
But When I use message.attach , it says EmailMessage object has no attribute attach
message.attach("certificate.pdf", new_file, "application/pdf")
or
message.Attachment("certificate.pdf", new_file, "application/pdf")
both says :EmailMessage object has no attribute attach/attachment
In the documentation there are examples of "Attachment".
Please help!
As far as I can see in the docs, there's class google.appengine.api.mail.Attachment, but class google.appengine.api.mail.EmailMessage does not have any method attach().
Class google.appengine.api.mail.EmailMessage does have an attachment property, and that's why it works when you initialize the email with attachments=[(foo,bar),(foo,bar)]. You're actually creating new instances of google.appengine.api.mail.Attachment (using the tuples as explained in the docs), adding them to an array, and using this array as the attachments property when initializing the email.
Notice that in the docs, when they write attachment = mail.Attachment('foo.jpg', 'data'), that mail is a reference to the imported google.appengine.api.mail, and not an instantiated mail object.
Getting back to your example (please note that I'm not a python dev and I have not tried it, I'm just looking through the docs and making assumptions), instead of
message.attach("certificate.pdf", new_file, "application/pdf")
you should probably go more on the way of
attachment1 = mail.Attachment("certificate.pdf", new_file, "application/pdf")
attachment2 = mail.Attachment("another_certificate.pdf", new_file, "application/pdf")
message.attachments = [attachment1, attachment2]
It's been years since I played with python, but feel free to explore this ideas and edit my answer in case I got anything wrong (or post your own answer).
The attributes of the EmailMessage class are assigned dynamically, like this*:
class EmailMessage(_EmailMessageBase):
def __init__(self, **kwargs):
for name, value in kwargs.items():
setattr(self, name, value)
Therefore, if attachments isn't passed to the constructor as a keyword argument, the instance has no attachments attribute, and you get an AttributeError if you try to reference it.
As Jofre observes in their answer, you can still assign to attachments directly:
message.attachments = [attachment1]
after the attachments attribute has been created, you can also append to it:
message.attachments.append(attachment2)
* This is a simplified version; in the real class the assignment is done in a separate method, but in essentially the same way.
I fixed the issue with send_mail function , one of the mistakes was not specifying ".pdf" for the file name instead I was only naming it "certificate"
Also If you have speficied any handlers in app.yaml for direct url access, make sure "application_readable" is set to true so that the file is accessible within the application:
- url: /certificate.((pdf)|(PDF))
static_files: pages/pdf/certificate.PDF
upload: pages/pdf/certificate.PDF
application_readable: true
additional_pdf = open(os.path.dirname(__file__) + '/../pages/pdf/certificate.pdf').read()
mail.send_mail(sender=EMAIL_SENDER,
to=['mmyemail#gmail.com'],
subject=subject,
body=theBody,
attachments=[(attachname, blob.archivoBlob),("certificate.pdf",additional_pdf)])
This is a duplicate to this question:
How to convert suds object to xml
But the question has not been answered: "totxt" is not an attribute on the Client class.
Unfortunately I lack of reputation to add comments. So I ask again:
Is there a way to convert a suds object to its xml?
I ask this because I already have a system that consumes wsdl files and sends data to a webservice. But now the customers want to alternatively store the XML as files (to import them later manually). So all I need are 2 methods for writing data: One writes to a webservice (implemented and tested), the other (not implemented yet) writes to files.
If only I could make something like this:
xml_as_string = My_suds_object.to_xml()
The following code is just an example and does not run. And it's not elegant. Doesn't matter. I hope you get the idea what I want to achieve:
I have the function "write_customer_obj_webservice" that works. Now I want to write the function "write_customer_obj_xml_file".
import suds
def get_customer_obj():
wsdl_url = r'file:C:/somepathhere/Customer.wsdl'
service_url = r'http://someiphere/Customer'
c = suds.client.Client(wsdl_url, location=service_url)
customer = c.factory.create("ns0:CustomerType")
return customer
def write_customer_obj_webservice(customer):
wsdl_url = r'file:C:/somepathhere/Customer.wsdl'
service_url = r'http://someiphere/Customer'
c = suds.client.Client(wsdl_url, location=service_url)
response = c.service.save(someparameters, None, None, customer)
return response
def write_customer_obj_xml_file(customer):
output_filename = r'C\temp\testxml'
# The following line is the problem. "to_xml" does not exist and I can't find a way to do it.
xml = customer.to_xml()
fo = open(output_filename, 'a')
try:
fo.write(xml)
except:
raise
else:
response = 'All ok'
finally:
fo.close()
return response
# Get the customer object always from the wsdl.
customer = get_customer_obj()
# Since customer is an object, setting it's attributes is very easy. There are very complex objects in this system.
customer.name = "Doe J."
customer.age = 42
# Write the new customer to a webservice or store it in a file for later proccessing
if later_processing:
response = write_customer_obj_xml_file(customer)
else:
response = write_customer_obj_webservice(customer)
I found a way that works for me. The trick is to create the Client with the option "nosend=True".
In the documentation it says:
nosend - Create the soap envelope but don't send. When specified, method invocation returns a RequestContext instead of sending it.
The RequestContext object has the attribute envelope. This is the XML as string.
Some pseudo code to illustrate:
c = suds.client.Client(url, nosend=True)
customer = c.factory.create("ns0:CustomerType")
customer.name = "Doe J."
customer.age = 42
response = c.service.save(someparameters, None, None, customer)
print response.envelope # This prints the XML string that would have been sent.
You have some issues in write_customer_obj_xml_file function:
Fix bad path:
output_filename = r'C:\temp\test.xml'
The following line is the problem. "to_xml" does not exist and I can't find a way to do it.
What's the type of customer? type(customer)?
xml = customer.to_xml() # to be continued...
Why mode='a'? ('a' => append, 'w' => create + write)
Use a with statement (file context manager).
with open(output_filename, 'w') as fo:
fo.write(xml)
Don't need to return a response string: use an exception manager. The exception to catch can be EnvironmentError.
Analyse
The following call:
customer = c.factory.create("ns0:CustomerType")
Construct a CustomerType on the fly, and return a CustomerType instance customer.
I think you can introspect your customer object, try the following:
vars(customer) # display the object attributes
help(customer) # display an extensive help about your instance
Another way is to try the WSDL URLs by hands, and see the XML results.
You may obtain the full description of your CustomerType object.
And then?
Then, with the attributes list, you can create your own XML. Use an XML template and fill it with the object attributes.
You may also found the magic function (to_xml) which do the job for you. But, not sure the XML format matches your need.
client = Client(url)
client.factory.create('somename')
# The last XML request by client
client.last_sent()
# The last XML response from Web Service
client.last_received()
I'm trying to write custom mail agent.
I am trying to fetch all mails, but my mailbox has polish letter in mailboxnames...
So this code (cut all prints from listing):
def parse_list_response(self, line):
list_response_pattern = re.compile(r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)')
line=line.decode(encoding='utf_8')
flags, delimiter, mailbox_name = list_response_pattern.match(line).groups()
mailbox_name = mailbox_name.strip('"')
return (flags, delimiter, mailbox_name)
def fetch_mails(self, from_who, since_when):
server = imaplib.IMAP4_SSL(self.hostname)
server.login(self.owner, self.password)
rc, mailboxes = server.list()
for line in mailboxes:
mailbox=self.parse_list_response(line)[2]
server.select(mailbox)
try:
messages = server.search('FROM "{}"'.format(from_who))
Gives me for example mailbox:
decoded = (\Flagged \HasNoChildren) "/" "[Gmail]/Oznaczone gwiazdk&AQU-"
See: &AQU-... it is polish "ą"
Question is how to get rid of this? I cannot find how to decode this bytecode
The encoding is IMAP4 Modified UTF-7, which is a convention used for international mailbox names, as defined in RFC3501, section 5.1.3.
Unfortunately, the imaplib module doesn't currently support it - although there are several issues on the python bug tracker that suggest that may change in the near future (e.g. issue 5305 and issue 22598).
Anyway, in the meantime, it looks like you will have to find a third-party package to handle this (e.g. imapclient).
I want to generate for my users temporary download link.
Is that ok if i use django to generate link using url patterns?
Could it be correct way to do that. Because can happen that I don't understand some processes how it works. And it will overflow my memory or something else. Some kind of example or tools will be appreciated. Some nginx, apache modules probably?
So, what i wanna to achieve is to make url pattern which depend on user and time. Decript it end return in view a file.
A simple scheme might be to use a hash digest of username and timestamp:
from datetime import datetime
from hashlib import sha1
user = 'bob'
time = datetime.now().isoformat()
plain = user + '\0' + time
token = sha1(plain)
print token.hexdigest()
"1e2c5078bd0de12a79d1a49255a9bff9737aa4a4"
Next you store that token in a memcache with an expiration time. This way any of your webservers can reach it and the token will auto-expire. Finally add a Django url handler for '^download/.+' where the controller just looks up that token in the memcache to determine if the token is valid. You can even store the filename to be downloaded as the token's value in memcache.
Yes it would be ok to allow django to generate the urls. This being exclusive from handling the urls, with urls.py. Typically you don't want django to handle the serving of files see the static file docs[1] about this, so get the notion of using url patterns out of your head.
What you might want to do is generate a random key using a hash, like md5/sha1. Store the file and the key, datetime it's added in the database, create the download directory in a root directory that's available from your webserver like apache or nginx... suggest nginx), Since it's temporary, you'll want to add a cron job that checks if the time since the url was generated has expired, cleans up the file and removes the db entry. This should be a django command for manage.py
Please note this is example code written just for this and not tested! It may not work the way you were planning on achieving this goal, but it works. If you want the dl to be pw protected also, then look into httpbasic auth. you can generate and remove entries on the fly in a httpd.auth file using htpasswd and the subprocess module when you create the link or at registration time.
import hashlib, random, datetime, os, shutil
# model to hold link info. has these fields: key (charfield), filepath (filepathfield)
# datetime (datetimefield), url (charfield), orgpath (filepathfield of the orignal path
# or a foreignkey to the files model.
from models import MyDlLink
# settings.py for the app
from myapp import settings as myapp_settings
# full path and name of file to dl.
def genUrl(filepath):
# create a onetime salt for randomness
salt = ''.join(['{0}'.format(random.randrange(10) for i in range(10)])
key = hashlib('{0}{1}'.format(salt, filepath).hexdigest()
newpath = os.path.join(myapp_settings.DL_ROOT, key)
shutil.copy2(fname, newpath)
newlink = MyDlink()
newlink.key = key
newlink.date = datetime.datetime.now()
newlink.orgpath = filepath
newlink.newpath = newpath
newlink.url = "{0}/{1}/{2}".format(myapp_settings.DL_URL, key, os.path.basename(fname))
newlink.save()
return newlink
# in commands
def check_url_expired():
maxage = datetime.timedelta(days=7)
now = datetime.datetime.now()
for link in MyDlink.objects.all():
if(now - link.date) > maxage:
os.path.remove(link.newpath)
link.delete()
[1] http://docs.djangoproject.com/en/1.2/howto/static-files/
It sounds like you are suggesting using some kind of dynamic url conf.
Why not forget your concerns by simplifying and setting up a single url that captures a large encoded string that depends on user/time?
(r'^download/(?P<encrypted_id>(.*)/$', 'download_file'), # use your own regexp
def download_file(request, encrypted_id):
decrypted = decrypt(encrypted_id)
_file = get_file(decrypted)
return _file
A lot of sites just use a get param too.
www.example.com/download_file/?09248903483o8a908423028a0df8032
If you are concerned about performance, look at the answers in this post: Having Django serve downloadable files
Where the use of the apache x-sendfile module is highlighted.
Another alternative is to simply redirect to the static file served by whatever means from django.