Send csv file via email django - django

I want to attach a csv file to an email.
i wrote a get method like this :
def get(self, request, *args, **kwargs):
serializer = self.get_serializer(self.get_queryset(), many=True)
file_format = request.query_params.get("file_format", "csv")
if file_format == "csv":
data_file = export_to_csv(
serializer.data,
list(self.get_serializer().Meta.fields),
"students"),
)
return data_file
this API generate a link to download the csv... but when I PDB the data_file I find out it is <HttpResponse status_code=200, "text/csv">
this the export_to_csv function :
def export_to_csv(data, titles, file_name):
#This function will export the data in the form of csv file
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = "attachment; filename={}.csv".format(file_name)
writer = csv.DictWriter(
response,
fieldnames=titles,
extrasaction="ignore",
)
# create dict from translation of headers
headers = {title: _(title).replace("_", " ").title() for title in titles}
# write translated headers
writer.writerow(headers)
# write data to csv file
for row in data:
writer.writerow(row)
return response
The question is how can I attach the generated csv to the email

Refer to this, it's a neat documentation
https://docs.djangoproject.com/en/4.1/topics/email/#django.core.mail.EmailMessage
you can attach filenames with email, that way you can send attachments.

Related

Django : How to upload csv file in unit test case using APIClient

I would like to write a unit test for a view on a Django REST Framework application. The test should upload a CSV file using the POST.
#staticmethod
def _file_upload(client, string, args, file_name):
base_path = os.path.dirname(os.path.realpath(__file__))
with open(base_path + file_name, 'rb') as data:
data = {
'file': data
}
response = client.post(reverse(string, args=[args]), data, format = "multipart")
return response.status_code, response.data
The above code I used which obviously doesn't work it shows the following error
Missing filename. Request should include a Content-Disposition header with a filename parameter.
The following code is the one that I want to test via unit testing.
class ChartOfAccounts(views.APIView):
parser_classes = (JSONParser, FileUploadParser)
def post(self, request, pk, *args, **kwargs):
request.FILES['file'].seek(0)
csv_data = CSVUtils.format_request_csv(request.FILES['file'])
try:
coa_data = CSVUtils.process_chart_of_accounts_csv(company, csv_data)
serializer = CoASerializer(coa_data, many=True)
if len(serializer.data) > 0:
return Utils.dispatch_success(request, serializer.data)
except Exception as e:
error = ["%s" % e]
return Utils.dispatch_failure(request, 'DATA_PARSING_ISSUE', error)
Any help regarding this is welcome. Thanks in advance
I have fixed my issue using the different approach with HTTP headers HTTP_CONTENT_DISPOSITION, HTTP_CONTENT_TYPE by this reference
And here is my code
#staticmethod
def _file_upload_csv( string, args, file_name):
base_path = os.path.dirname(os.path.realpath(__file__))
data = open(base_path + file_name, 'rb')
data = SimpleUploadedFile(content = data.read(),name = data.name,content_type='multipart/form-data')
factory = RequestFactory()
user = User.objects.get(username=UserConstant.ADMIN_USERNAME)
view = ChartOfAccounts.as_view()
content_type = 'multipart/form-data'
headers= {
'HTTP_CONTENT_TYPE': content_type,
'HTTP_CONTENT_DISPOSITION': 'attachment; filename='+file_name}
request = factory.post(reverse(string, args=[args]),{'file': data},
**headers)
force_authenticate(request, user=user)
response = view(request, args)
return response.status_code, response.data
**headers done the trick...
Here's what i did
#patch("pandas.read_csv")
#patch("pandas.DataFrame.to_sql")
def test_upload_csv_success(self, mock_read_csv, mock_to_sql) -> None:
"""Test uploading a csv file"""
file_name = "test.csv"
# Open file in write mode (Arrange)
with open(file_name, "w") as file:
writer = csv.writer(file)
# Add some rows in csv file
writer.writerow(["name", "area", "country_code2", "country_code3"])
writer.writerow(
["Albania", 28748, "AL", "ALB"],
)
writer.writerow(
["Algeria", 2381741, "DZ", "DZA"],
)
writer.writerow(
["Andorra", 468, "AD", "AND"],
)
# open file in read mode
data = open(file_name, "rb")
# Create a simple uploaded file
data = SimpleUploadedFile(
content=data.read(), name=data.name, content_type="multipart/form-data"
)
# Perform put request (Act)
res = self.client.put(CSV_URL, {"file_name": data}, format="multipart")
# Mock read_csv() and to_sql() functions provided by pandas module
mock_read_csv.return_value = True
mock_to_sql.return_value = True
# Assert
self.assertEqual(res.status_code, status.HTTP_201_CREATED)
self.assertEqual(res.data, "Data set uploaded")
# Delete the test csv file
os.remove(file_name)

Django REST Framework: upload image and rename it

My application is used to register restaurants in the system. Along with some data (like restaurant name) the frontend is sending restaurant logo. Using this code:
class Base64ImageField(serializers.ImageField):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.default_error_messages['image_not_png'] = _(
'Only .png files are supported.'
)
def to_internal_value(self, data):
if isinstance(data, six.string_types):
if 'data:' in data and ';base64,' in data:
header, data = data.split(';base64,')
try:
decoded_file = base64.b64decode(data)
except TypeError:
self.fail('invalid_image')
file_name = str(uuid.uuid4())
file_extension = self.get_file_extension(file_name, decoded_file)
if file_extension is not 'png':
self.fail('image_not_png')
complete_file_name = '%s.%s'.format(file_name, file_extension)
data = ContentFile(decoded_file, name=complete_file_name)
return super().to_internal_value(data)
def get_file_extension(self, file_name, decoded_file):
return imghdr.what(file_name, decoded_file)
I was able to decode and save the logo to the disk with some randomly generated name. Now I'm wondering how to name that file properly. By 'properly' I mean that I want to name the logo file the same as is the restaurant name, which is problematic, because to_internal_value doesn't have access to anything else than the file data The questions is: where should I put the code to rename the file to be the same as the restaurant name that came in the same request?

How do I receive XML file via POST in django

I'm trying to send XML file to django view using requests. I think the file is being sent by doing:
headers = {'Content-Type': 'application/xml'}
response = requests.post('http://localhost:8000/file/', data=file, headers=headers)
in view I have:
class ReceiveFile(TemplateView):
#method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
print request.read()
return HttpResponse('')
So how do I read file here in view and save it as xml again? request.read() gives me path from where file was sent.
Best, Blake
You are sending the file path, rather than the contents of the file.
You will need to send the contents of the file instead.
Assuming that your file contains valid XML:
headers = {'Content-Type': 'application/xml'}
open_file = open(file,'r')
file_contents = open_file.read()
response = requests.post('http://localhost:8000/file/', data=file_contents, headers=headers)

Django file upload progress get cache returns None

Django file upload progress process json request getting null response
view.py
def upload_progress(request):
"""
A view to report back on upload progress.
Return JSON object with information about the progress of an upload.
Copied from:
http://djangosnippets.org/snippets/678/
See upload.py for file upload handler.
"""
#import ipdb
#ipdb.set_trace()
progress_id = ''
if 'X-Progress-ID' in request.GET:
progress_id = request.GET['X-Progress-ID']
elif 'X-Progress-ID' in request.META:
progress_id = request.META['X-Progress-ID']
if progress_id:
from django.utils import simplejson
cache_key = "%s_%s" % (request.META['REMOTE_ADDR'], progress_id)
data = cache.get(cache_key)
return HttpResponse(simplejson.dumps(data))
UploadProgressCachedHandler.py
from django.core.files.uploadhandler import FileUploadHandler
from django.core.cache import cache
class UploadProgressCachedHandler(FileUploadHandler):
"""
Tracks progress for file uploads.
The http post request must contain a header or query parameter, 'X-Progress-ID'
which should contain a unique string to identify the upload to be tracked.
Copied from:
http://djangosnippets.org/snippets/678/
See views.py for upload_progress function...
"""
def __init__(self, request=None):
super(UploadProgressCachedHandler, self).__init__(request)
self.progress_id = None
self.cache_key = None
def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None):
self.content_length = content_length
if 'X-Progress-ID' in self.request.GET :
self.progress_id = self.request.GET['X-Progress-ID']
elif 'X-Progress-ID' in self.request.META:
self.progress_id = self.request.META['X-Progress-ID']
if self.progress_id:
self.cache_key = "%s_%s" % (self.request.META['REMOTE_ADDR'], self.progress_id )
cache.set(self.cache_key, {
'length': self.content_length,
'uploaded' : 0
})
def new_file(self, field_name, file_name, content_type, content_length, charset=None):
pass
def receive_data_chunk(self, raw_data, start):
if self.cache_key:
data = cache.get(self.cache_key)
data['uploaded'] += self.chunk_size
cache.set(self.cache_key, data)
#cache.set(self.cache_key, 5000)
return raw_data
def file_complete(self, file_size):
pass
def upload_complete(self):
if self.cache_key:
cache.delete(self.cache_key)
Iam setting cache with uploadProgressCacheHandler. But when tries to retrieve via json request .The data returning None object.'cache_key' is generating correctly.
Please help.
I guess you are trying to make the JSON request when the flow already has reached upload_complete. At that point cache_key has been deleted from cache (that's why it is empty).
Are you using the Django development server? I think this built-in server only allows to handle one request at time, which in your situation means that first the upload is completed and following the JSON request is processed. You can try with another server (ex: Apache) able to handle multiple requests.

pyPDF merging and displaying as httpresponse through django

I'm having trouble incorporating pyPDF logic to merge two pdf files into my django site. I have written code that works to merge files when run in a python file on the local server(but I need to explicitly identify which files to merge:
from pyPdf import PdfFileReader, PdfFileWriter
output = PdfFileWriter()
input1 = PdfFileReader(file("abc_form0.pdf", "rb"))
input2 = PdfFileReader(file("abc_form1.pdf", "rb"))
total_pages = input1.getNumPages()
total_pages1 = input2.getNumPages()
for page in xrange(total_pages):
output.addPage(input1.getPage(page))
for page in xrange(total_pages1):
output.addPage(input2.getPage(page))
outputStream = file("output.pdf", "wb")
output.write(outputStream)
outputStream.close()
This code (from my django site) works to display a single PDF. However, when more than one PDF is selected, only the final PDF selected displays, hence the need to integrate pyPDF to create one file containing all requested files.
class ABCAdmin(admin.ModelAdmin):
actions = ['print_selected_pdf']
def create_pdf(self, request, queryset):
response = HttpResponse(mimetype="application/pdf")
response['Content-Disposition'] = 'attachment; filename=form.pdf'
for obj in queryset:
response.write(obj.form)
ABC.objects.filter(pk=obj.pk).update(user=request.user,pdf_printed="1",request_time=time.strftime("%H:%M:%S"),request_date=datetime.datetime.today())
return response
def print_selected_pdf(self, request, queryset):
# prints the pdfs for those that are selected,
# regardless if the pdf_printed field is true or false
qs = queryset.filter(pdf_printed__exact=0)
return self.create_pdf(request, qs)
I'm struggling with finding a way to combine these two methods. What I have tried is the following, but get an IO error [Errno 2] No such file or directory: 'obj.form'. So it is not reading in obj.form - I need to find a way for it to read in these objects correctly. Anyway, this is what I've tried.
def create_pdf(self, request, queryset):
response = HttpResponse(mimetype="application/pdf")
response['Content-Disposition'] = 'attachment; filename=form.pdf'
for obj in queryset:
output = PdfFileWriter()
input = PdfFileReader(file("obj.form","rb"))
total_pages = input.getNumPages()
for page in xrange(total_pages):
output.addPage(input.GetPage(page))
outputStream = file("output.pdf", "wb")
response.write(outputStream)
outputStream.close()
ABC.objects.filter(pk=obj.pk).update(user=request.user,pdf_printed="1",request_time=time.strftime("%H:%M:%S"),request_date=datetime.datetime.today())
return response
def print_selected_pdf(self, request, queryset):
# prints the pdfs for those that are selected,
# regardless if the pdf_printed field is true or false
qs = queryset.filter(pdf_printed__exact=0)
return self.create_pdf(request, qs)
As always, thanks for any help or tips you may be able to provide!
I used stringIO in the output stream to get around this. I also had to define each page of the PDF form to be added to the output. This works to iterate for any number of forms that I need to be requested.
def create_form(self, request, queryset):
response = HttpResponse(mimetype="application/pdf")
response['Content-Disposition'] = 'attachment; filename=form.pdf'
output = PdfFileWriter()
for obj in queryset:
input = PdfFileReader(cStringIO.StringIO(obj.form))
output.addPage(input.getPage(0))
output.addPage(input.getPage(1))
output.addPage(input.getPage(2))
output.addPage(input.getPage(3))
ABC_Self.objects.filter(pk=obj.pk).update(user=request.user,pdf_printed="1",request_time=time.strftime("%H:%M:%S"),request_date=datetime.datetime.today())
outputStream = cStringIO.StringIO()
output.write(outputStream)
response.write(outputStream.getvalue())
return response
All you need to do is provide the full absolute path to your PDF files, e.g.
/home/joseph/form.pdf
or
c:/home/joseph/form.pdf
etc.