How to extracting long views code block in a smaller method? - python

I have a Django application. And I have a long method where a user can upload a file and the content of the file will be shown in textarea.
Because of the S.O.L.I.D principle. The code that is responsible for extracting the data from the file has to be in a seperate method.
So this is the views.py:
class ReadingFile(View):
def get(self, request):
form = ProfileForm()
return render(request, "main/create_profile.html", {
"form": form
})
def extractingtextfromimage():
pass
def post(self, request):
submitted_form = ProfileForm(request.POST, request.FILES)
content = ''
if submitted_form.is_valid():
uploadfile = UploadFile(image=request.FILES["upload_file"])
name_of_file = str(request.FILES['upload_file'])
uploadfile.save()
print('path of the file is:::', uploadfile.image.name)
with open(os.path.join(settings.MEDIA_ROOT,
f"{uploadfile.image}"), 'r') as f:
print("Now its type is ", type(name_of_file))
print(uploadfile.image.path)
# reading PDF file
if name_of_file.endswith('.pdf'):
pdfFile = wi(filename= uploadfile.image.path , resolution=300)
text_factuur_verdi = []
image = pdfFile.convert('jpeg')
imageBlobs = []
for img in image.sequence:
imgPage = wi(image=img)
imageBlobs.append(imgPage.make_blob('jpeg'))
for imgBlob in imageBlobs:
image = Image.open(io.BytesIO(imgBlob))
text = pytesseract.image_to_string(image, lang='eng')
text_factuur_verdi.append(text)
content = text_factuur_verdi
print(text_factuur_verdi)
# ENDING Reading pdf file
else:
content = f.read()
print(content)
return render(request, "main/create_profile.html", {
'form': ProfileForm(),
"content": content
})
return render(request, "main/create_profile.html", {
"form": submitted_form,
})
And it is about the comment:
reading PDF file
till: # ENDING Reading pdf file
How to seperate that block of code in a seperate method?
I am realy stuck about that part.

Frankly, I don't understand what is your problem.
I can't test it but I would simply copy code to function and send some values as parameters, and use return to send result - so it could be something like this
class ReadingFile(View):
# ... other functions ...
def read_pdf_file(self, uploadfile):
pdfFile = wi(filename=uploadfile.image.path , resolution=300)
text_factuur_verdi = []
image = pdfFile.convert('jpeg')
imageBlobs = []
for img in image.sequence:
imgPage = wi(image=img)
imageBlobs.append(imgPage.make_blob('jpeg'))
for imgBlob in imageBlobs:
image = Image.open(io.BytesIO(imgBlob))
text = pytesseract.image_to_string(image, lang='eng')
text_factuur_verdi.append(text)
content = text_factuur_verdi
print(text_factuur_verdi)
return content
def post(self, request):
# ... code ...
# reading PDF file
if name_of_file.endswith('.pdf'):
content = self.read_pdf_file(uploadfile)
# ENDING Reading pdf file
else:
content = f.read()
# ... code ...
BTW:
I would reduce code to single for-loop, and send only filename instead of uploadfile
def read_pdf_file(self, filename):
content = []
pdf_file = wi(filename=filename, resolution=300)
all_images = pdf_file.convert('jpeg')
for image in all_images.sequence:
image = wi(image=image)
image = image.make_blob('jpeg')
image = Image.open(io.BytesIO(image))
text = pytesseract.image_to_string(image, lang='eng')
content.append(text)
#print(content)
#content = '\n'.join(content) # convert list to single string
return content
# ... later ...
content = self.read_pdf_file(uploadfile.image.path)
And I think it should be
if uploadfile.image.path.endswith('.pdf'):
content = self.read_pdf_file(uploadfile.image.path)
else:
with open(os.path.join(settings.MEDIA_ROOT, uploadfile.image.path)) as f:
content = f.read()

Related

Set in memory image in PDF with Flask

I am just trying to draw an image to a pdf. This image is loaded from my mongodb database through an API.
I tried this solution but it raises the following error:
2022-09-14T21:05:56.561767+00:00 app[web.1]: remote_file = ImageReader(urlopen(img_url)).read()
2022-09-14T21:05:56.561767+00:00 app[web.1]: AttributeError: 'ImageReader' object has no attribute 'read'
Also, if you know a better way to set this image in the pdf let me know please.
This is the code I am using:
def get(self, usermail, dog_name):
client = pymongo.MongoClient('mongodb://uri')
filter={'UserMail':usermail,'title':dog_name}
result = client['db']['Todo'].find(
filter=filter
)
json_response = json.dumps(list(result), default=json_util.default)
dog = json.loads(json_response)
df = pd.DataFrame(dog).to_dict()
dog_img = df['DogImg'][0]
img_url = 'https://url' + dog_img
dog_age = df['Age'][0]
dog_desc = df['DogDescription'][0]
dog_name = df['title'][0]
dog_breed = df['Breed'][0]
buf = io.BytesIO()
c = canvas.Canvas(buf, pagesize=letter)
#c.drawImage(logo, 30, 700, width=50, height=50)
c.setFont("Helvetica", 20)
c.drawString(100, 720, dog_name)
buf.seek(0)
remote_file = ImageReader(urlopen(img_url)).read()
memory_file = io.BytesIO(remote_file)
buf.seek(0)
new_pdf = PdfFileReader(buf)
existing_pdf = PdfFileReader(memory_file)
pdf = PdfFileWriter()
page = existing_pdf.getPage(0)
page.mergePage(new_pdf.getPage(0))
pdf.addPage(page)
outputStream = open("destination.pdf", "wb")
pdf.write(outputStream)
outfile = io.BytesIO()
pdf.write(outfile)
outfile.seek(0)
return send_file(outfile, mimetype='application/pdf')
Try using the following
from reportlab.platypus import Image, SimpleDocTemplate
from flask import send_file
pdf_filename = 'original.pdf'
new_filename = "new_filename.pdf"
pdf_template = SimpleDocTemplate(pdf_filename, pagesize=letter,topMargin=0, bottomMargin=0, leftMargin=0, rightMargin=0)
story1 = []
img_url = 'https://url' + dog_img
# or from locally in project
# img_url = os.path.join(cwd, "dog_img")
img1 = Image(img_url, width=600, height=780)
story1.append(img1)
pdf_template.build(story1)
return send_file(pdf_filename, attachment_filename=new_filename, as_attachment=True)

Django HEIC to PNG conversion

I'm trying to convert heic to png, which I successfully achieved in Python with the help of Wand library. I also saved the file locally to see, if the convertion worked, and it did. The problem is that Django's serializer cant take in Wand image, and I have to convert it to InMemoryUploadedFile. Whatever I do, I still can't make the serializator to take in the converted image.
views.py update_post()
#api_view(['PUT'])
#permission_classes([IsAuthenticated])
def update_post(request, id):
image = request.FILES['image']
print(image.size, image.name, image.file,
image.content_type, image.field_name)
if image.content_type == 'image/heif':
img = Image(file=image)
img.format = 'png'
img_io = io.BytesIO()
img.save(file=img_io)
filename = image.name.replace('.heic', '.png')
img.save(filename=filename)
img_file = InMemoryUploadedFile(
img_io,
'image',
filename,
'image/png',
sys.getsizeof(img_io),
None)
print(img_file.size, img_file.name, img_file.file,
img_file.content_type, img_file.field_name)
image = img_file
#request.data['image'] = img_file
#request.FILES['image'] = img_file
# data = request.data
# print(img_file, image)
loggedin_user = request.user.username
post = Post.objects.get(id=id)
post_user = post.user
if (str(post_user) == str(loggedin_user)):
serializer = PostSerializer(
instance=post, data={'caption': request.data['caption'], 'image': image})
if serializer.is_valid():
print(serializer.validated_data)
serializer.save()
else:
print('achjo')
print(serializer.data)
return Response(status=status.HTTP_200_OK)

Passing image PIL object from view to other view django

How do I pass a PIL Image object from one view def to another view def? It is possible to use a reverse() function. I would like to display the changed image on the imageSizeConverter view without saving. My code is
def imageSizeConverter(request):
if request.method == 'POST':
item = request.FILES.get('imgInp')
width = int(request.POST.get('width'))
height = int(request.POST.get('height'))
proportion = request.POST.get('proportion')
root = tk.Tk()
root.withdraw()
file_path = filedialog.askdirectory()
name = request.POST.get('fname')
print(file_path)
img = Image.open(item)
if(proportion == 'on'):
img.thumbnail((width, height))
else:
img.resize((width, height))
resultFileName, file_extension = os.path.splitext(name)
if not file_extension:
filename, file_extension = os.path.splitext(item.name)
nazwa = file_path + "/" + resultFileName + file_extension
img.save(nazwa)
return render(request, "app/imageSizeConverter.html",
{
'title':'Zmiana rozmiaru',
'year':datetime.now().year,
'image': reverse('show_image')
})
def show_image(request):
response = HttpResponse(content_type="image/png")
img.save(response, "PNG")
return response
Edit
This is the solution to my problem:
output = BytesIO()
img.save(output, format='JPEG')
im_data = output.getvalue()
data_url = 'data:image/jpg;base64,' + base64.b64encode(im_data).decode()
return render(request, "app/imageSizeConverter.html",
{
'title':'Zmiana rozmiaru',
'year':datetime.now().year,
'image': data_url
})
And use on the template:
<img src="{{ image }}" alt=""/>

django download images in zip file

I use this code in DJANGO framework to can some user download images.
this code work fine and download image every time to press download some user.
but this code download absolute images I need to zip this images for any user download.
def download_image(request, id):
product_image=MyModel.objects.get(pk=id)
product_image_url = product_image.upload.url
wrapper = FileWrapper(open(settings.MEDIA_ROOT+ product_image_url[6:], 'rb'))
content_type = mimetypes.guess_type(product_image_url)[0]
response = HttpResponse(wrapper, content_type=content_type)
response['Content-Disposition'] = "attachment; filename=%s" % product_image_url
return response
is easy to change this code to download images in zip file ?
Try the following:
def download_image(request, id):
product_image=MyModel.objects.get(pk=id)
product_image_url = product_image.upload.url
image_path = settings.MEDIA_ROOT+ product_image_url[6:]
image_name = 'whatevername.png'; # Get your file name here.
with ZipFile('export.zip', 'w') as export_zip:
export_zip.write(image_path, image_name)
wrapper = FileWrapper(open('export.zip', 'rb'))
content_type = 'application/zip'
content_disposition = 'attachment; filename=export.zip'
response = HttpResponse(wrapper, content_type=content_type)
response['Content-Disposition'] = content_disposition
return response

PIL to Django ImageField

I try to create an image from an url and save it in my django model. If the first part works fine, I do not know how to associate the generated file to my object.
This is my function to generate the image file:
def get_remote_image(image_url, merchant_product_path):
im = None
name = ''
r = requests.get(image_url, stream=True)
if r.status_code == 200:
name = urlparse(image_url).path.split('/')[-1]
full_path = os.path.join(settings.MEDIA_ROOT, merchant_product_path)
if not os.path.exists(full_path):
os.makedirs(full_path)
im = Image.open(r.raw)
if im.mode != "RGB":
im = im.convert("RGB")
im.thumbnail((500, 500), Image.ANTIALIAS)
im.save(full_path + name, 'JPEG')
return {'im': im, 'name': name}
And now, the part to associate this file to my object:
i = get_remote_image(row['pict'], m.get_products_media_path())
obj, created = ProductLine.objects.update_or_create(
...
...
...
)
if i['im'] is not None:
try:
obj.main_picture.save(
i['name'],
ContentFile(i['im']),
save=True)
except TypeError:
continue
This code works but unfortunately, mu pictures are created in the correct folder, objects are created/update but each one has no picture file :(
Can someone tell me what's wrong ?
I've finally found a solution:
def get_remote_image(image_url):
im = None
name = ''
r = requests.get(image_url, stream=True)
if r.status_code == 200:
name = urlparse(image_url).path.split('/')[-1]
i = Image.open(r.raw)
buffer = BytesIO()
if i.mode != "RGB":
i = i.convert("RGB")
i.thumbnail((500, 500), Image.ANTIALIAS)
i.save(buffer, format='JPEG')
im = InMemoryUploadedFile(
buffer,
None,
name,
'image/jpeg',
buffer.tell(),
None)
return {'im': im, 'name': name}
and then:
obj, created = ProductLine.objects.update_or_create(
...
...
...
)
i = get_remote_image(row['pict'])
obj.main_picture.save(
os.path.join(m.get_products_image_path(), i['name']),
i['im'],
save=True)
Hope this will help some other users in this situation.
With a model like :
class ProductLine(models.Model):
name = models.CharField(max_length=250, null=True)
image = models.ImageField(null=True)
You can directly link the picture on your computer using is path instead of his binary content.
obj, created = ProductLine.objects.update_or_create(...)
obj.image.name = "/path/to/the/file"
obj.save()

Categories