227 lines
6.6 KiB
Python
227 lines
6.6 KiB
Python
import os
|
|
import io
|
|
import fitz
|
|
import qrcode
|
|
import datetime
|
|
import logging
|
|
import urllib.parse
|
|
from typing import Optional
|
|
from dotenv import load_dotenv
|
|
from fastapi.responses import FileResponse
|
|
from fastapi import FastAPI, File, UploadFile, Response
|
|
|
|
|
|
load_dotenv()
|
|
SERVICE_URL = os.getenv('SERVICE_URL')
|
|
DESTINATION = os.getenv('DESTINATION')
|
|
CHUNK_SIZE = int(os.getenv('CHUNK_SIZE'))
|
|
|
|
log = logging.getLogger(__name__)
|
|
app = FastAPI()
|
|
|
|
|
|
def urlencode(str):
|
|
return urllib.parse.quote(str)
|
|
|
|
|
|
def urldecode(str):
|
|
return urllib.parse.unquote(str)
|
|
|
|
def make_qrcode(qr_data):
|
|
"""
|
|
Generate a QR code from the given qr_data.
|
|
|
|
Args:
|
|
qr_data (str): The data to be encoded in the QR code.
|
|
|
|
Returns:
|
|
io.BytesIO: The QR code image as a BytesIO object.
|
|
"""
|
|
qr = qrcode.QRCode(
|
|
version=1,
|
|
error_correction=qrcode.constants.ERROR_CORRECT_L,
|
|
box_size=6,
|
|
border=2,
|
|
)
|
|
qr.add_data(qr_data)
|
|
qr.make(fit=True)
|
|
|
|
img = qr.make_image(fill_color="black", back_color="white")
|
|
fp = io.BytesIO()
|
|
img.save(fp, "PNG")
|
|
return fp
|
|
|
|
def create_qr_rectangle(preset, page):
|
|
"""
|
|
Create a QR rectangle based on the preset and page provided.
|
|
|
|
Parameters:
|
|
- preset (str): The preset for the QR rectangle.
|
|
- page (Page): The page on which the QR rectangle will be created.
|
|
|
|
Returns:
|
|
- rect (Rect): The created QR rectangle.
|
|
"""
|
|
w = page.rect.width # page width
|
|
|
|
if preset == "inv1":
|
|
margin = 15
|
|
left = w*0.40
|
|
elif preset == "inv2":
|
|
margin = 20
|
|
left = w*0.88
|
|
else:
|
|
margin = 0
|
|
left = 0
|
|
|
|
rect = fitz.Rect(left, margin, left + 65, margin + 65)
|
|
|
|
return rect
|
|
|
|
async def chunked_copy(src, dst):
|
|
"""
|
|
Asynchronously copies the contents of the source file to the destination file
|
|
in chunks.
|
|
|
|
Args:
|
|
src: The source file object.
|
|
dst: The destination file path.
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
await src.seek(0)
|
|
with open(dst, "wb") as buffer:
|
|
while True:
|
|
contents = await src.read(CHUNK_SIZE)
|
|
if not contents:
|
|
log.info(f"Src completely consumed\n")
|
|
break
|
|
log.info(f"Consumed {len(contents)} bytes from Src file\n")
|
|
buffer.write(contents)
|
|
|
|
@app.post("/signinvoice/")
|
|
async def sign_invoice(
|
|
file: UploadFile = File(...),
|
|
preset: Optional[str] = None,
|
|
code_content: Optional[str] = None,
|
|
company: Optional[str] = None,
|
|
inv_type: Optional[str] = None,
|
|
inv_series: Optional[str] = None,
|
|
inv_num: Optional[str] = None,
|
|
in_all: bool = False
|
|
):
|
|
"""
|
|
Sign an invoice file and generate a QR code for it.
|
|
|
|
Args:
|
|
file (UploadFile): The file to be signed.
|
|
preset (str, optional): The preset for the QR code.
|
|
code_content (str, optional): The content for the QR code.
|
|
company (str, optional): The company name.
|
|
inv_type (str, optional): The invoice type.
|
|
inv_series (str, optional): The invoice series.
|
|
inv_num (str, optional): The invoice number.
|
|
in_all (bool): Whether to include the QR code in all pages.
|
|
|
|
Returns:
|
|
FileResponse: The signed invoice file with QR code.
|
|
"""
|
|
|
|
inv_code = "{}_{}_{}_{}".format(company, inv_type, inv_series, inv_num).replace(" ", "_").replace(",", "").replace("#", "")
|
|
inv_url = "{}/getinvoice/?inv_code={}".format(SERVICE_URL, inv_code)
|
|
|
|
#print("SIGNED URL: {}".format(inv_url))
|
|
|
|
temp_path = os.path.join(DESTINATION, f"{inv_code}_temp.pdf")
|
|
full_path = os.path.join(DESTINATION, f"{inv_code}.pdf")
|
|
|
|
await chunked_copy(file, temp_path)
|
|
|
|
if not code_content:
|
|
code_content = inv_url
|
|
|
|
qr_image = make_qrcode(code_content)
|
|
doc = fitz.open(temp_path)
|
|
|
|
add_qr = True
|
|
for page in doc:
|
|
if add_qr:
|
|
rect = create_qr_rectangle(preset, page)
|
|
if not page.is_wrapped:
|
|
page.wrap_contents()
|
|
page.insert_image(rect, stream=qr_image)
|
|
if not in_all and page.number == 0:
|
|
add_qr = False
|
|
|
|
doc.save(full_path, deflate=True, garbage=3)
|
|
|
|
if os.path.isfile(temp_path):
|
|
os.remove(temp_path)
|
|
print("{} : Returned Signed Invoice".format(datetime.datetime.now()))
|
|
return FileResponse(full_path)
|
|
|
|
|
|
@app.get("/getinvoice/")
|
|
async def get_invoice(
|
|
company: Optional[str] = None,
|
|
inv_code: Optional[str] = None,
|
|
inv_type: Optional[str] = None,
|
|
inv_series: Optional[str] = None,
|
|
inv_num: Optional[str] = None,
|
|
search_file: bool = False
|
|
):
|
|
"""
|
|
An asynchronous function to retrieve an invoice file based on various parameters.
|
|
|
|
Args:
|
|
company (Optional[str]): The company name.
|
|
inv_code (Optional[str]): The invoice code.
|
|
inv_type (Optional[str]): The invoice type.
|
|
inv_series (Optional[str]): The invoice series.
|
|
inv_num (Optional[str]): The invoice number.
|
|
search_file (bool): Flag to indicate whether to search for the file.
|
|
|
|
Returns:
|
|
Union[FileResponse, Dict[str, str]]: Returns the invoice file if found, otherwise returns a message.
|
|
"""
|
|
if search_file:
|
|
inv_code = "{}_{}_{}_{}".format(company, inv_type, inv_series, inv_num).replace(" ", "_").replace(",", "").replace("#", "")
|
|
|
|
if inv_code:
|
|
full_path = os.path.join(DESTINATION, "{}.pdf".format(inv_code))
|
|
if os.path.isfile(full_path):
|
|
print("FETCHING INVOICE: {} | {}".format(inv_code, full_path))
|
|
return FileResponse(full_path)
|
|
else:
|
|
print("{} : No invoice found".format(datetime.datetime.now()))
|
|
return {"message": "No invoice found"}
|
|
|
|
|
|
@app.get("/createqr/")
|
|
async def create_qr_code(code_content: Optional[str] = None, token: Optional[str] = None):
|
|
"""
|
|
Create a QR code from the given code_content.
|
|
|
|
Args:
|
|
code_content (str, optional): The content to be encoded in the QR code. Defaults to None.
|
|
|
|
Returns:
|
|
Response: The QR code image as a Response object with media type "image/png".
|
|
"""
|
|
if token == 'ZPOclCF5od59SgW6PLM2':
|
|
if code_content:
|
|
qr_image = make_qrcode(code_content)
|
|
print("{} : QR Created".format(datetime.datetime.now()))
|
|
return Response(content=qr_image.getvalue(), media_type="image/png")
|
|
else:
|
|
print("{} : QR Code Content not provided".format(datetime.datetime.now()))
|
|
return "QR Code Content not provided"
|
|
else:
|
|
return "Unauthorized"
|
|
|
|
|
|
@app.get('/favicon.ico', include_in_schema=False)
|
|
async def favicon():
|
|
return FileResponse("favicon.ico")
|