Ejemplo n.º 1
0
class SFTPStorageFile(File):
    def __init__(self, name, storage, mode):
        self._name = name
        self._storage = storage
        self._mode = mode
        self._is_dirty = False
        self.file = BytesIO()
        self._is_read = False

    @property
    def size(self):
        if not hasattr(self, '_size'):
            self._size = self._storage.size(self._name)
        return self._size

    def read(self, num_bytes=None):
        if not self._is_read:
            self.file = self._storage._read(self._name)
            self._is_read = True

        return self.file.read(num_bytes)

    def write(self, content):
        if 'w' not in self._mode:
            raise AttributeError("File was opened for read-only access.")
        self.file = BytesIO(content)
        self._is_dirty = True
        self._is_read = True

    def close(self):
        if self._is_dirty:
            self._storage._save(self._name, self)
        self.file.close()
Ejemplo n.º 2
0
class SFTPStorageFile(File):
    def __init__(self, name, storage, mode):
        self._name = name
        self._storage = storage
        self._mode = mode
        self._is_dirty = False
        self.file = BytesIO()
        self._is_read = False

    @property
    def size(self):
        if not hasattr(self, '_size'):
            self._size = self._storage.size(self._name)
        return self._size

    def read(self, num_bytes=None):
        if not self._is_read:
            self.file = self._storage._read(self._name)
            self._is_read = True

        return self.file.read(num_bytes)

    def write(self, content):
        if 'w' not in self._mode:
            raise AttributeError("File was opened for read-only access.")
        self.file = BytesIO(content)
        self._is_dirty = True
        self._is_read = True

    def close(self):
        if self._is_dirty:
            self._storage._save(self._name, self)
        self.file.close()
Ejemplo n.º 3
0
def render(data, width, height, force=True, padding=None, overlays=(), overlay_sources=(),
           overlay_tints=(), overlay_sizes=None, overlay_positions=None, mask=None, mask_source=None,
           center=".5,.5", format=IMAGE_DEFAULT_FORMAT, quality=IMAGE_DEFAULT_QUALITY, fill=None, background=None,
           tint=None, pre_rotation=None, post_rotation=None, crop=True, grayscale=False):
    """
    Rescale the given image, optionally cropping it to make sure the result image has the specified width and height.
    """

    if not isinstance(data, six.string_types):
        input_file = BytesIO(data)
    else:
        input_file = StringIO(data)

    img = pil.open(input_file)
    if img.mode != "RGBA":
        img = img.convert("RGBA")

    if width is None:
        width = img.size[0]
    if height is None:
        height = img.size[1]

    img = do_rotate(img, pre_rotation)

    if crop:
        img = resizeCrop(img, width, height, center, force)
    else:
        img = resizeScale(img, width, height, force)

    if grayscale:
        img = do_grayscale(img)
    do_tint(img, tint)
    img = do_fill(img, fill, width, height)
    img = do_background(img, background)
    do_mask(img, mask, mask_source)
    img = do_overlays(img, overlays, overlay_tints, overlay_sources, overlay_sizes, overlay_positions)
    img = do_padding(img, padding)
    img = do_rotate(img, post_rotation)

    tmp = BytesIO()

    if not format.upper() in ALPHA_FORMATS:
        img = img.convert("RGB")

    img.save(tmp, format, quality=quality)
    tmp.seek(0)
    output_data = tmp.getvalue()
    input_file.close()
    tmp.close()

    return output_data
Ejemplo n.º 4
0
def render(data, width, height, force=True, padding=None, overlays=(), overlay_sources=(),
                 overlay_tints=(), overlay_sizes=None, overlay_positions=None, mask=None, mask_source=None,
                 center=".5,.5", format=IMAGE_DEFAULT_FORMAT, quality=IMAGE_DEFAULT_QUALITY, fill=None, background=None,
                 tint=None, pre_rotation=None, post_rotation=None, crop=True, grayscale=False):
    """
    Rescale the given image, optionally cropping it to make sure the result image has the specified width and height.
    """

    if not isinstance(data, six.string_types):
        input_file = BytesIO(data)
    else:
        input_file = StringIO(data)

    img = pil.open(input_file)
    if img.mode != "RGBA":
        img = img.convert("RGBA")

    if width is None:
        width = img.size[0]
    if height is None:
        height = img.size[1]

    img = do_rotate(img, pre_rotation)

    if crop:
        img = resizeCrop(img, width, height, center, force)
    else:
        img = resizeScale(img, width, height, force)

    if grayscale:
        img = do_grayscale(img)
    do_tint(img, tint)
    img = do_fill(img, fill, width, height)
    img = do_background(img, background)
    do_mask(img, mask, mask_source)
    img = do_overlays(img, overlays, overlay_tints, overlay_sources, overlay_sizes, overlay_positions)
    img = do_padding(img, padding)
    img = do_rotate(img, post_rotation)

    tmp = BytesIO()

    if not format.upper() in ALPHA_FORMATS:
        img = img.convert("RGB")

    img.save(tmp, format, quality=quality)
    tmp.seek(0)
    output_data = tmp.getvalue()
    input_file.close()
    tmp.close()

    return output_data
Ejemplo n.º 5
0
def add_user(name, bio):
    u = User.objects.get_or_create(username=name)[0]
    u.save()
    p = UserProfile.objects.get_or_create(user=u)[0]
    p.bio = bio

    # https://stackoverflow.com/questions/54891829/typeerror-memoryview-a-bytes-like-object-is-required-not-jpegimagefile
    image = 'populate_images/' + name + '.jpg'
    img_in = Image.open(image)
    buf = BytesIO()
    img_in.save(buf, 'jpeg')
    buf.seek(0)
    p.profile_image.save(name + '.jpeg', buf, True)
    buf.close()
    img_in.close()
    return p
Ejemplo n.º 6
0
def downloadSheetPage(request, pk):
	""" View for dowloading a sheet """

	student = getStudent(request.user)
	sheet = getSheetInstance(pk) # Get the file
	
	# Check if the user can have access to it (if he is part of the lesson)
	if sheet.lesson not in student.classroom.lessons.all():
		raise PermissionDenied()

	files = sheet.fileSet

	if len(files) == 1:
		uploadedFile = files[0]

		contentType = uploadedFile.contentType
		fileName = sheet.name + uploadedFile.extension
		data = uploadedFile.file

		response = HttpResponse(data.read(), content_type=contentType)
		response['Content-Disposition'] = 'attachment; filename="{}"'.format(fileName)

		data.close()
	else:
		contentType = 'application/zip'
		fileName = sheet.name + '.zip'

		zipStream = BytesIO()
		zipFile = ZipFile(zipStream, 'w')

		for uploadedFile in files:
			filePath = join(settings.MEDIA_ROOT, uploadedFile.file.name)
			archiveName = uploadedFile.file.name.replace('sheets/', '').replace('-point-', '.')

			zipFile.write(filePath, archiveName)

		zipFile.close()
		zipStream.seek(0)

		response = HttpResponse(zipStream, content_type=contentType)
		response['Content-Disposition'] = 'attachment; filename="{}"'.format(fileName)

		zipStream.close()
	return response
Ejemplo n.º 7
0
class FTPStorageFile(File):
    def __init__(self, name, storage, mode):
        self.name = name
        self._storage = storage
        self._mode = mode
        self._is_dirty = False
        self.file = BytesIO()
        self._is_read = False

    @property
    def size(self):
        if not hasattr(self, "_size"):
            self._size = self._storage.size(self.name)
        return self._size

    def readlines(self):
        if not self._is_read:
            self._storage._start_connection()
            self.file = self._storage._read(self.name)
            self._is_read = True
        return self.file.readlines()

    def read(self, num_bytes=None):
        if not self._is_read:
            self._storage._start_connection()
            self.file = self._storage._read(self.name)
            self._is_read = True
        return self.file.read(num_bytes)

    def write(self, content):
        if "w" not in self._mode:
            raise AttributeError("File was opened for read-only access.")
        self.file = BytesIO(content)
        self._is_dirty = True
        self._is_read = True

    def close(self):
        if self._is_dirty:
            self._storage._start_connection()
            self._storage._put_file(self.name, self)
            self._storage.disconnect()
        self.file.close()
Ejemplo n.º 8
0
class CouchDBFile(File):
    """
    CouchDBFile - a Django File-like class for CouchDB documents.
    """

    def __init__(self, name, storage, mode):
        self._name = name
        self._storage = storage
        self._mode = mode
        self._is_dirty = False

        try:
            self._doc = self._storage.get_document(name)

            tmp, ext = os.path.split(name)
            if ext:
                filename = "content." + ext
            else:
                filename = "content"
            attachment = self._storage.db.get_attachment(self._doc, filename=filename)
            self.file = BytesIO(attachment)
        except couchdb.client.ResourceNotFound:
            if 'r' in self._mode:
                raise ValueError("The file cannot be reopened.")
            else:
                self.file = BytesIO()
                self._is_dirty = True

    @property
    def size(self):
        return self._doc['size']

    def write(self, content):
        if 'w' not in self._mode:
            raise AttributeError("File was opened for read-only access.")
        self.file = BytesIO(content)
        self._is_dirty = True

    def close(self):
        if self._is_dirty:
            self._storage._put_file(self._name, self.file.getvalue())
        self.file.close()
class LibCloudFile(File):
    """File inherited class for libcloud storage objects read and write"""
    def __init__(self, name, storage, mode):
        self.name = name
        self._storage = storage
        self._mode = mode
        self._is_dirty = False
        self._file = None

    def _get_file(self):
        if self._file is None:
            data = self._storage._read(self.name)
            self._file = BytesIO(data)
        return self._file

    def _set_file(self, value):
        self._file = value

    file = property(_get_file, _set_file)

    @property
    def size(self):
        if not hasattr(self, '_size'):
            self._size = self._storage.size(self.name)
        return self._size

    def read(self, num_bytes=None):
        return self.file.read(num_bytes)

    def write(self, content):
        if 'w' not in self._mode:
            raise AttributeError("File was opened for read-only access.")
        self.file = BytesIO(content)
        self._is_dirty = True

    def close(self):
        if self._is_dirty:
            self._storage._save(self.name, self.file)
        self.file.close()
Ejemplo n.º 10
0
class LibCloudFile(File):
    """File inherited class for libcloud storage objects read and write"""
    def __init__(self, name, storage, mode):
        self.name = name
        self._storage = storage
        self._mode = mode
        self._is_dirty = False
        self._file = None

    def _get_file(self):
        if self._file is None:
            data = self._storage._read(self.name)
            self._file = BytesIO(data)
        return self._file

    def _set_file(self, value):
        self._file = value

    file = property(_get_file, _set_file)

    @property
    def size(self):
        if not hasattr(self, '_size'):
            self._size = self._storage.size(self.name)
        return self._size

    def read(self, num_bytes=None):
        return self.file.read(num_bytes)

    def write(self, content):
        if 'w' not in self._mode:
            raise AttributeError("File was opened for read-only access.")
        self.file = BytesIO(content)
        self._is_dirty = True

    def close(self):
        if self._is_dirty:
            self._storage._save(self.name, self.file)
        self.file.close()
Ejemplo n.º 11
0
def add_event(request, me):
    """
    创建活动
    """
    if request.method == 'POST':
        # 获取表单提交数据
        name = request.POST['name']
        time = request.POST['time']
        address = request.POST['address']
        introduce = request.POST['introduce']
        event_img = request.FILES['event_img']
        # 生成二维码

        # 保存表单数据
        event = Event.objects.create(name=name,
                                     time=time,
                                     address=address,
                                     introduce=introduce,
                                     event_img=event_img)
        qr = qrcode.make(
            'https://pinkslash.metatype.cn/wechat_login/?status=provider_{0}'.
            format(event.id))
        buf = BytesIO()
        qr.save(buf)
        qr_data = buf.getvalue()
        buf.write(qr_data)
        qr_img = InMemoryUploadedFile(file=buf,
                                      field_name=None,
                                      name='event.png',
                                      content_type='image/png',
                                      size=len(qr_data),
                                      charset=None)
        # 保存表单数据
        event.qrcode = qr_img
        event.save()
        buf.close()

    return redirect('/events/')
Ejemplo n.º 12
0
def add_food(request, me):
    if request.method == 'POST':
        # 获取表单提交数据
        name = request.POST['name']
        credit = request.POST['credit']
        # event_id = request.POST['event_id']

        food_img = request.FILES['food_img']

        event = me.event

        # 保存表单数据
        food = Food.objects.create(name=name,
                                   provider=me,
                                   credit=credit,
                                   event=event,
                                   food_img=food_img)
        # 生成二维码

        qr = qrcode.make(
            'https://pinkslash.metatype.cn/wechat_login/?status=purchase_{0}_{1}'
            .format(event.id, food.id))
        buf = BytesIO()
        qr.save(buf)
        qr_data = buf.getvalue()
        buf.write(qr_data)
        qr_img = InMemoryUploadedFile(file=buf,
                                      field_name=None,
                                      name='food.png',
                                      content_type='image/png',
                                      size=len(qr_data),
                                      charset=None)
        food.qrcode = qr_img
        food.save()
        buf.close()

    return redirect('/foods?eid={0}'.format(event.id))
Ejemplo n.º 13
0
    def list(self, request, project):
        """
        GET method implementation for log slicer

        Receives a line range and job_id and returns those lines
        """
        job_id = request.query_params.get("job_id")
        log_name = request.query_params.get("name")
        if log_name:
            log_names = [log_name]
        else:
            log_names = ["buildbot_text", "builds-4h"]
        format = 'json' if log_name == 'mozlog_json' else 'text'

        file = None

        start_line = request.query_params.get("start_line")
        end_line = request.query_params.get("end_line")
        if not start_line or not end_line:
            return Response(
                "``start_line`` and ``end_line`` parameters are both required",
                400)

        try:
            start_line = abs(int(start_line))
            end_line = abs(int(end_line))
        except ValueError:
            return Response("parameters could not be converted to integers",
                            400)

        if start_line >= end_line:
            return Response("``end_line`` must be larger than ``start_line``",
                            400)

        try:
            job = Job.objects.get(repository__name=project,
                                  project_specific_id=job_id)
        except Job.DoesNotExist:
            return Response("Job does not exist", 404)

        try:
            url = JobLog.objects.filter(
                job=job, name__in=log_names)[0:1].values_list('url',
                                                              flat=True)[0]
        except JobLog.DoesNotExist:
            return Response("Job log does not exist", 404)

        try:
            file = filesystem.get(url)
            if not file:
                r = make_request(url)
                try:
                    file = gzip.GzipFile(fileobj=BytesIO(r.content))
                    # read 16 bytes, just to make sure the file is gzipped
                    file.read(16)
                    file.seek(0)
                    filesystem.set(url, file.fileobj)
                except IOError:
                    # file is not gzipped, but we should still store / read
                    # it as such, to save space
                    file = BytesIO(r.content)
                    gz_file_content = BytesIO()
                    with gzip.GzipFile('none', 'w',
                                       fileobj=gz_file_content) as gz:
                        gz.write(r.content)
                    filesystem.set(url, gz_file_content)
            else:
                file = gzip.GzipFile(fileobj=file)

            lines = []
            for i, line in enumerate(file):
                if i < start_line:
                    continue
                elif i >= end_line:
                    break

                if format == 'json':
                    lines.append({"data": json.loads(line), "index": i})
                else:
                    lines.append({"text": line, "index": i})

            return Response(lines)

        finally:
            if file:
                file.close()
Ejemplo n.º 14
0
    def list(self, request, project):
        """
        GET method implementation for log slicer

        Receives a line range and job_id and returns those lines
        """
        job_id = request.query_params.get("job_id")
        log_name = request.query_params.get("name")
        if log_name:
            log_names = [log_name]
        else:
            log_names = ["buildbot_text", "builds-4h"]
        format = 'json' if log_name == 'mozlog_json' else 'text'

        file = None

        start_line = request.query_params.get("start_line")
        end_line = request.query_params.get("end_line")
        if not start_line or not end_line:
            return Response("``start_line`` and ``end_line`` parameters are both required", 400)

        try:
            start_line = abs(int(start_line))
            end_line = abs(int(end_line))
        except ValueError:
            return Response("parameters could not be converted to integers", 400)

        if start_line >= end_line:
            return Response("``end_line`` must be larger than ``start_line``", 400)

        try:
            job = Job.objects.get(repository__name=project,
                                  project_specific_id=job_id)
        except Job.DoesNotExist:
            return Response("Job does not exist", 404)

        try:
            url = JobLog.objects.filter(
                job=job, name__in=log_names)[0:1].values_list('url',
                                                              flat=True)[0]
        except JobLog.DoesNotExist:
            return Response("Job log does not exist", 404)

        try:
            file = filesystem.get(url)
            if not file:
                r = make_request(url)
                try:
                    file = gzip.GzipFile(fileobj=BytesIO(r.content))
                    # read 16 bytes, just to make sure the file is gzipped
                    file.read(16)
                    file.seek(0)
                    filesystem.set(url, file.fileobj)
                except IOError:
                    # file is not gzipped, but we should still store / read
                    # it as such, to save space
                    file = BytesIO(r.content)
                    gz_file_content = BytesIO()
                    with gzip.GzipFile('none', 'w', fileobj=gz_file_content) as gz:
                        gz.write(r.content)
                    filesystem.set(url, gz_file_content)
            else:
                file = gzip.GzipFile(fileobj=file)

            lines = []
            for i, line in enumerate(file):
                if i < start_line:
                    continue
                elif i >= end_line:
                    break

                if format == 'json':
                    lines.append({"data": json.loads(line), "index": i})
                else:
                    lines.append({"text": line, "index": i})

            return Response(lines)

        finally:
            if file:
                file.close()
Ejemplo n.º 15
0
def generate_pdf(request,
                 obj,
                 bid_item_dict,
                 invoice,
                 employee,
                 save_to_disk=False,
                 return_file_object=False):
    buff = BytesIO()

    # The page width totals 18.6cm
    doc = SimpleDocTemplate(buff,
                            rightMargin=2 * cm,
                            leftMargin=2 * cm,
                            topMargin=1.5 * cm,
                            bottomMargin=3.75 * cm)

    def _header_footer(canvas, doc):
        # Save the state of our canvas so we can draw on it
        canvas.saveState()
        styles = getSampleStyleSheet()

        # Header
        header = Paragraph(
            'This is a multi-line header.  It goes on every page.   ' * 5,
            styles['Normal'])
        w, h = header.wrap(doc.width, doc.topMargin)
        header.drawOn(canvas, doc.leftMargin, doc.height + doc.topMargin - h)

        # Footer
        footer = Paragraph('Thank You For Your Business', styles['Normal'])
        w, h = footer.wrap(doc.width, doc.bottomMargin)
        footer.drawOn(canvas, doc.leftMargin, h)

        # Release the canvas
        canvas.restoreState()

    story = []

    # Styles
    styles = getSampleStyleSheet()
    styles.add(ParagraphStyle(name='Center', alignment=TA_CENTER))
    styles.add(ParagraphStyle(name='Right', alignment=TA_RIGHT))
    styles.add(ParagraphStyle(name='Left', alignment=TA_LEFT))
    styles.add(
        ParagraphStyle(name='Line_Data',
                       alignment=TA_LEFT,
                       fontSize=8,
                       leading=7))
    styles.add(
        ParagraphStyle(name='Line_Data_Small',
                       alignment=TA_LEFT,
                       fontSize=7,
                       leading=8))
    styles.add(
        ParagraphStyle(name='Line_Data_Medium',
                       alignment=TA_LEFT,
                       fontSize=10,
                       leading=8))
    styles.add(
        ParagraphStyle(name='Line_Data_Large',
                       alignment=TA_LEFT,
                       fontSize=12,
                       leading=12))
    styles.add(
        ParagraphStyle(name='Line_Data_Large_Right',
                       alignment=TA_RIGHT,
                       fontSize=12,
                       leading=12))
    styles.add(
        ParagraphStyle(name='Line_Data_Large_Center',
                       alignment=TA_CENTER,
                       fontSize=12,
                       leading=12))
    styles.add(
        ParagraphStyle(name='Invoice_Date',
                       alignment=TA_LEFT,
                       fontSize=12,
                       leading=12))
    styles.add(
        ParagraphStyle(name='Line_Data_Largest',
                       fontName='Times-BoldItalic',
                       alignment=TA_CENTER,
                       fontSize=22,
                       leading=15))
    styles.add(
        ParagraphStyle(name='Line_Label',
                       fontSize=10,
                       leading=12,
                       alignment=TA_LEFT))
    styles.add(
        ParagraphStyle(name='Line_Label_Center',
                       fontSize=7,
                       alignment=TA_CENTER))

    # Add Company Address, Logo and Invoice Info
    company_paragraph = """
        179 Marvy ST<br />
        Lino Lakes, MN 55014<br />
        (612) 508-2484 <br />
        [email protected] <br />
        MN License: BC690748
        """

    logo = os.path.join(settings.STATIC_ROOT, 'img/logo.jpg')
    denominator = 5
    image = Image(logo, width=800 / denominator, height=269 / denominator)

    if invoice:
        proposal_invoice_paragraph = """
            Date: {}<br />
            Invoice #: {:04d} <br />
        """.format(datetime.date.today().strftime('%x'), obj.id)
    else:
        proposal_invoice_paragraph = """
            Submitted By: <br />
            Tom Madsen <br />
            Date: {}<br />
            Proposal #: {:04d} <br />
        """.format(datetime.date.today().strftime('%x'), obj.id)

    data1 = [[
        Paragraph(company_paragraph, styles['Line_Data_Large']), image,
        Paragraph(proposal_invoice_paragraph, styles['Line_Data_Large'])
    ]]

    t1 = Table(data1, colWidths=(6.7 * cm, 8 * cm, 4.6 * cm))
    t1.setStyle(TableStyle([
        ('VALIGN', (0, 0), (-1, -1), 'TOP'),
    ]))

    story.append(t1)

    # Add Proposal or Invoice Title to PDF
    if invoice:
        pdf_type = 'Invoice'
    elif employee:
        pdf_type = 'Employee Copy'
    else:
        pdf_type = 'Proposal'

    data1 = [[Paragraph(pdf_type, styles["Line_Data_Largest"])]]

    t1 = Table(data1, colWidths=(18.6 * cm))
    t1.setStyle(TableStyle([('VALIGN', (0, 0), (-1, -1), 'TOP')]))

    story.append(t1)
    story.append(Spacer(2, 32))

    # Add Customer Info and Job Description
    telephone = obj.customer.telephone
    telephone = "({}) {}-{}".format(telephone[:3], telephone[3:6],
                                    telephone[6:])

    if obj.customer.company_name:
        company = "{}<br />".format(obj.customer.company_name)
    else:
        company = ""

    location_paragraph = """
        {first} {last}<br />
        {company}
        {street}<br />
        {city}, {state} {zip}<br />
        {telephone}<br />
        {email}""".format(first=obj.customer.first_name,
                          last=obj.customer.last_name,
                          company=company,
                          street=obj.address.street,
                          city=obj.address.city,
                          state=obj.address.state,
                          zip=obj.address.zip,
                          telephone=telephone,
                          email=obj.customer.email)

    if obj.billto_city_st_zip:  # check if this is an alternate billto field populated
        if len(obj.billto_telephone) == 10:
            billto_telephone = obj.billto_telephone
            billto_telephone = "({}) {}-{}".format(billto_telephone[:3],
                                                   billto_telephone[3:6],
                                                   billto_telephone[6:])
        else:
            billto_telephone = obj.billto_telephone

        billto_paragraph = """
        {name}<br />
        {street}<br />
        {city_st_zip}<br />
        {telephone}""".format(name=obj.billto_name,
                              street=obj.billto_street,
                              city_st_zip=obj.billto_city_st_zip,
                              telephone=billto_telephone)
    else:
        billto_paragraph = location_paragraph

    description_paragraph = obj.description

    if invoice:
        data1 = [[
            Paragraph('Bill To', styles["Line_Data_Large"]),
            Paragraph('Job Address', styles["Line_Data_Large"])
        ],
                 [
                     Paragraph(billto_paragraph, styles["Line_Data_Large"]),
                     Paragraph(location_paragraph, styles["Line_Data_Large"])
                 ]]

        t1 = Table(data1, colWidths=(9.3 * cm, 9.3 * cm))
        t1.setStyle(
            TableStyle([('VALIGN', (0, 0), (-1, -1), 'TOP'),
                        ('BACKGROUND', (0, 0), (1, 0), colors.lightgrey)]))

        story.append(t1)
        story.append(Spacer(4, 20))

        data1 = [[Paragraph('Job Description', styles["Line_Data_Large"])],
                 [Paragraph(description_paragraph, styles["Line_Data_Large"])]]

        t1 = Table(data1, colWidths=(18.6 * cm))
        t1.setStyle(
            TableStyle([('VALIGN', (0, 0), (-1, -1), 'TOP'),
                        ('BACKGROUND', (0, 0), (1, 0), colors.lightgrey)]))

        story.append(t1)

    else:  # Proposal

        data1 = [[
            Paragraph('Job Address', styles["Line_Data_Large"]),
            Paragraph('Job Description', styles["Line_Data_Large"])
        ],
                 [
                     Paragraph(billto_paragraph, styles["Line_Data_Large"]),
                     Paragraph(description_paragraph,
                               styles["Line_Data_Large"])
                 ]]

        t1 = Table(data1, colWidths=(7 * cm, 11.6 * cm))
        t1.setStyle(
            TableStyle([('VALIGN', (0, 0), (-1, -1), 'TOP'),
                        ('BACKGROUND', (0, 0), (1, 0), colors.lightgrey)]))

        story.append(t1)

        if obj.notes:
            story.append(Spacer(2, 10))
            data1 = [[Paragraph('Notes', styles["Line_Data_Large"])],
                     [Paragraph(obj.notes, styles["Line_Data_Large"])]]

            t1 = Table(data1, colWidths=(18.6 * cm))
            t1.setStyle(
                TableStyle([('VALIGN', (0, 0), (-1, -1), 'TOP'),
                            ('BACKGROUND', (0, 0), (1, 0), colors.lightgrey)]))

            story.append(t1)

    # Add Bid Items to PDF
    story.append(Spacer(4, 32))

    for job_name, items in bid_item_dict.items():
        title = [[
            Paragraph(job_name, styles["Line_Data_Large"]),
            Paragraph('', styles["Line_Data_Large"])
        ]]

        t1 = Table(title, colWidths=(15 * cm, 3.6 * cm))
        t1.setStyle(
            TableStyle([('INNERGRID', (0, 0), (-1, -1), 0.25, colors.black),
                        ('BOX', (0, 0), (-1, -1), .25, colors.black),
                        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
                        ('BACKGROUND', (0, 0), (-1, -1), colors.lightgrey)]))

        story.append(t1)

        if employee:  # Add quantities but remove pricing
            data1 = [[
                Paragraph(str(item.description), styles["Line_Data_Large"]),
                Paragraph(str(item.quantity), styles["Line_Data_Large_Right"])
            ] for item in items]

            t1 = Table(data1, colWidths=(15 * cm, 3.6 * cm))
            t1.setStyle(
                TableStyle([
                    ('INNERGRID', (0, 0), (-1, -1), 0.25, colors.black),
                    ('BOX', (0, 0), (-1, -1), 0.25, colors.black),
                    ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
                ]))

            story.append(t1)
            story.append(Spacer(4, 32))

        else:  # Add pricing but not quantity for end customer
            data1 = [[
                Paragraph(str(item.description), styles["Line_Data_Large"]),
                Paragraph(str("{0:.2f}".format(round(item.total, 2))),
                          styles["Line_Data_Large_Right"])
            ] for item in items]

            t1 = Table(data1, colWidths=(15 * cm, 3.6 * cm))
            t1.setStyle(
                TableStyle([
                    ('INNERGRID', (0, 0), (-1, -1), 0.25, colors.black),
                    ('BOX', (0, 0), (-1, -1), 0.25, colors.black),
                    ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
                ]))

            story.append(t1)

            # Calculate total per job and add to PDF
            total = items.aggregate(Sum('total'))['total__sum']
            total_text = "{} Total".format(job_name)
            data1 = [[
                Paragraph(total_text, styles["Line_Data_Large"]),
                Paragraph(str("${0:.2f}".format(total)),
                          styles['Line_Data_Large_Right'])
            ]]

            t1 = Table(data1, colWidths=(15 * cm, 3.6 * cm))
            t1.setStyle(
                TableStyle([
                    ('INNERGRID', (0, 0), (-1, -1), 0.25, colors.black),
                    ('BOX', (0, 0), (-1, -1), .25, colors.black),
                    ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
                    ('BACKGROUND', (0, 0), (-1, -1), colors.lightgrey)
                ]))

            story.append(t1)
            story.append(Spacer(4, 32))

    # Calculate Bid Total
    items = BidItem.objects.all().filter(bid=obj.id)
    bid_total = items.aggregate(Sum('total'))['total__sum']

    if not bid_total:
        bid_total = 0

    if invoice:  # Calculate Balance Due and Payment History for Invoice
        payments = Payment.objects.all().filter(bid=obj.id)
        payment_total = payments.aggregate(Sum('amount'))['amount__sum']

        cents = Decimal('0.01')
        if payment_total:
            remaining_balance = Decimal(bid_total - payment_total).quantize(
                cents, ROUND_HALF_UP)
        else:
            remaining_balance = bid_total

        data1 = [
            [Paragraph('Invoice Summary', styles["Line_Data_Large"]), None],
            [
                Paragraph('Initial Balance', styles["Line_Data_Large"]),
                Paragraph(str("{0:.2f}".format(round(bid_total, 2))),
                          styles["Line_Data_Large_Right"])
            ],
        ]

        data2 = [[
            Paragraph(
                str("Received Payment on {}".format(
                    payment.date.strftime('%x'))), styles["Line_Data_Large"]),
            Paragraph(str("-{0:.2f}".format(round(payment.amount, 2))),
                      styles["Line_Data_Large_Right"])
        ] for payment in payments]

        last_row = len(data2) + 2

        data3 = [[
            Paragraph('Remaining Balance Due', styles["Line_Data_Large"]),
            Paragraph(str("${0:.2f}".format(round(remaining_balance, 2))),
                      styles["Line_Data_Large_Right"])
        ]]

        all_data = data1 + data2 + data3

        t1 = Table(all_data, colWidths=(14 * cm, 4.6 * cm))
        t1.setStyle(
            TableStyle([
                ('BOX', (0, 0), (-1, -1), .25, colors.black),
                ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
                ('BACKGROUND', (0, 0), (-1, 0),
                 colors.lightgrey),  # we propose
                ('BACKGROUND', (0, last_row), (-1, last_row),
                 colors.lightgrey),  # payment outline
            ]))

        story.append(KeepTogether(t1))

    elif employee:  # If employee skip rest of bid info
        pass

    else:  # Proposal

        if obj.custom_down_payment and obj.custom_down_payment != -1:
            down_payment = obj.custom_down_payment
        elif obj.custom_down_payment == -1:
            down_payment = 0
        else:
            if bid_total:
                down_payment = bid_total / 2
            else:
                down_payment = 0

        if bid_total:
            cents = Decimal('0.01')
            final_payment = Decimal(bid_total - down_payment).quantize(
                cents, ROUND_HALF_UP)
        else:
            final_payment = 0

        if not bid_total:
            bid_total = 0

        we_propose = 'Hereby to furnish material and labor complete in accordance with above specifications,' \
                     ' for the sum of'

        acceptance = """The above prices, specifications and conditions are satisfactory and are hereby accepted.
        You are authorized to do the work as specified. Payment will be made as outlined above.
        I have received a copy of the Pre-Lien notice."""

        data1 = [
            [Paragraph('We Propose', styles["Line_Data_Large"]), None],
            [
                Paragraph(we_propose, styles["Line_Data_Large"]),
                Paragraph(str("${0:.2f}".format(round(bid_total, 2))),
                          styles["Line_Data_Large_Right"])
            ],
            [Paragraph('Payment Outline', styles["Line_Data_Large"]), None],
            [
                Paragraph('Deposit', styles["Line_Data_Large"]),
                Paragraph(str("${0:.2f}".format(round(down_payment, 2))),
                          styles["Line_Data_Large_Right"])
            ],
            [
                Paragraph(
                    'Remaining Balance Due Upon Completion of the Contract',
                    styles["Line_Data_Large"]),
                Paragraph(str("${0:.2f}".format(round(final_payment, 2))),
                          styles["Line_Data_Large_Right"])
            ],
            [
                Paragraph('Acceptance of Proposal', styles["Line_Data_Large"]),
                None
            ],
            [Paragraph(acceptance, styles["Line_Data_Large"]), None],
            [
                Paragraph('Signature:', styles["Line_Label"]),
                Paragraph('Date:', styles["Line_Label"])
            ],
            [
                Paragraph(
                    'X__________________________________________________________________',
                    styles["Line_Label"]),
                Paragraph('_____________________', styles["Line_Label"])
            ],
        ]

        t1 = Table(data1, colWidths=(14 * cm, 4.6 * cm))
        t1.setStyle(
            TableStyle([
                ('BOX', (0, 0), (-1, -1), .25, colors.black),
                ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
                ('BACKGROUND', (0, 0), (-1, 0),
                 colors.lightgrey),  # we propose
                ('BACKGROUND', (0, 2), (-1, 2),
                 colors.lightgrey),  # payment outline
                ('BACKGROUND', (0, 5), (-1, 5),
                 colors.lightgrey),  # acceptance of proposal
                ('SPAN', (0, 6),
                 (-1, 6)),  # span acceptance text across both columns
                ('BOTTOMPADDING', (0, 7), (-1, 7), 40)
            ]))

        story.append(KeepTogether(t1))

        # Add Pre-Lien Notice to PDF
        story.append(PageBreak())

        pre_lien_notice = """
        <br />
        ANY PERSON OR COMPANY SUPPLYING LABOR OR MATERIALS FOR THIS IMPROVEMENT TO YOUR PROPERTY MAY FILE A LIEN
        AGAINST YOUR PROPERTY IF THAT PERSON OR COMPANY IS NOT PAID FOR THE CONTRIBUTIONS.<br /><br />

        UNDER MINNESOTA LAW, YOU HAVE THE RIGHT TO PAY PERSONS WHO SUPPLIED LABOR OR MATERIALS FOR THIS IMPROVEMENT
         DIRECTLY AND DEDUCT THIS AMOUNT FROM OUR CONTRACT PRICE, OR WITHHOLD THE AMOUNTS DUE THEM FROM US UNTIL
         120 DAYS AFTER COMPLETION OF THE IMPROVEMENT UNLESS WE GIVE YOU A LIEN WAIVER SIGNED BY PERSONS WHO SUPPLIED
         ANY LABOR OR MATERIAL FOR THE IMPROVEMENT AND WHO GAVE YOU TIMELY NOTICE.
        """

        data1 = [[
            Paragraph('PRE-LIEN NOTICE', styles["Line_Data_Large_Center"])
        ], [Paragraph(pre_lien_notice, styles["Line_Data_Large"])]]

        t1 = Table(data1)
        story.append(t1)

    # doc.build(story, onFirstPage=_header_footer, onLaterPages=_header_footer, canvasmaker=NumberedCanvas)
    doc.build(story, canvasmaker=NumberedCanvas)

    pdf = buff.getvalue()
    buff.close()

    if return_file_object:
        # For send pdf to employee which isnt stored to database, return the file object
        return pdf

    if save_to_disk:
        myfile = ContentFile(pdf)
        db_model = PDFImage()
        db_model.bid = obj
        if invoice:
            filename_temp = 'invoice'
        else:
            filename_temp = 'proposal'

        db_model.filename.save(filename_temp, myfile)
        messages.success(request, "PDF was saved successfully!")
        return redirect('bid_app:bid_detail', pk=obj.id)

    if invoice:
        filename = "{}_invoice_{}".format(
            obj.customer.__str__().replace(' ', '_').replace(',', '').lower(),
            datetime.date.today())
    else:
        filename = "{}_proposal_{}".format(
            obj.customer.__str__().replace(' ', '_').replace(',', '').lower(),
            datetime.date.today())
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'filename={}.pdf'.format(filename)
    response.write(pdf)

    return response