diff --git a/README.md b/README.md index 8b237bc..9bdc144 100644 --- a/README.md +++ b/README.md @@ -5,4 +5,6 @@ This application requires Python3 to be installed with all the packages listed in `requirements.txt`, as well as the following software: * Perl and the erect2cubic package -* Hugin (incl. Nona) \ No newline at end of file +* Hugin (incl. Nona) +* Java and zxing +* PythonMagick \ No newline at end of file diff --git a/converter/nona.py b/converter/nona.py index 160b8a1..cdf9485 100644 --- a/converter/nona.py +++ b/converter/nona.py @@ -1,15 +1,17 @@ import tempfile import subprocess -import PIL.Image +import PythonMagick -def convert(infile): +def convert(infile, extension): pto = tempfile.NamedTemporaryFile() - image = tempfile.NamedTemporaryFile(suffix="." + infile.split(".")[-1].split("/")[-1]) - with open(infile, "rb") as inimage: - image.write(inimage.read()) + image = tempfile.NamedTemporaryFile(suffix=extension) + image.write(infile.read()) erect = ["erect2cubic", f"--erect={image.name}", f"--ptofile={pto.name}", "--filespec=PNG_m"] subprocess.run(erect) tiles = tempfile.TemporaryDirectory() nona = ["nona", pto.name, "-o", tiles.name + "/out"] subprocess.run(nona) - return PIL.Image.open(tiles.name + "/out0005.png") \ No newline at end of file + image = PythonMagick.Image(tiles.name + "/out0005.png") + geo = PythonMagick.Geometry(int(image.baseColumns() / 3), int(image.baseColumns() / 3), int(image.baseColumns() / 3), int(image.baseColumns() / 3)) + image.crop(geo) + return image \ No newline at end of file diff --git a/core/models.py b/core/models.py index 1e6a3e7..8b7501f 100644 --- a/core/models.py +++ b/core/models.py @@ -78,7 +78,7 @@ class Content(Model): class Media(Model): uuid = UUIDField(primary_key=True) - code = ForeignKey(Content, CASCADE) + content = ForeignKey(Content, CASCADE) @classmethod def generate_uuid(cls): diff --git a/core/urls/api.py b/core/urls/api.py index 9472596..13ebac7 100644 --- a/core/urls/api.py +++ b/core/urls/api.py @@ -15,7 +15,7 @@ Including another URLconf """ from django.urls import path -from core.views import APICreateSeries, APISeriesByIDDispatcher, APICodesBySeriesIDDispatcher, APICreateCodes, APICodeByID +from core.views import APICreateSeries, APISeriesByIDDispatcher, APICodesBySeriesIDDispatcher, APICreateCodes, APICodeByID, APIAnalyzeContent urlpatterns = [ path('series/', APICreateSeries.as_view()), @@ -24,4 +24,5 @@ urlpatterns = [ path('codes/', APICreateCodes.as_view()), path('codes//', APICodeByID.as_view()), path('codes///', APICodeByID.as_view()), + path('content/', APIAnalyzeContent.as_view()), ] diff --git a/core/views/__init__.py b/core/views/__init__.py index 74b2295..502a317 100644 --- a/core/views/__init__.py +++ b/core/views/__init__.py @@ -14,7 +14,12 @@ from io import BytesIO import urllib.request -from core.models import Series, APIKey, Code +import boto3 + +from core.models import Series, APIKey, Code, Media, Content +from customsettings import AWS_ACCESS_KEY_ID, AWS_BUCKET, AWS_SECRET_ACCESS_KEY +from converter import convert +from reader import read_code class APIView(View): @method_decorator(csrf_exempt) @@ -224,13 +229,62 @@ class APICodeByID(CodeAPIView): class APIAnalyzeContent(APIView): def post(self, request, *args, **kwargs): - url = request.body.decode("utf-8") + data = json.loads(request.body.decode("utf-8")) + + if data.get("url"): + try: + URLValidator()(data["url"]) + content = BytesIO(urllib.request.urlopen(data["url"]).read()) + extension = "." + data["url"].split(".")[-1] + except: + return JsonResponse({"error": "Failed to retrieve file %s" % data["url"]}) - try: - val = URLValidator().val(url) - except ValidationError: - return JsonResponse({"error": "Failed to validate URL"}) + else: + try: + session = boto3.Session(aws_access_key_id=AWS_ACCESS_KEY_ID, aws_secret_access_key=AWS_SECRET_ACCESS_KEY) + s3 = session.resource("s3") + bucket = s3.Bucket(AWS_BUCKET) + content = BytesIO() + bucket.download_fileobj(data["path"], content) + extension = "." + data["path"].split(".")[-1] + except: + return JsonResponse({"error": "Failed to retrieve file %s" % data["path"]}) - content = BytesIO(urllib.request.urlopen('url').read()) + content.seek(0) - \ No newline at end of file + down = convert(content, extension) + + literal = read_code(down) + + if literal: + code = None + + if len(literal.split(":")) == 3: + seriesid = literal.split(":")[1] + codeid = literal.split(":")[2] + try: + code = Code.objects.get(id=codeid, series=Series.objects.get(id=seriesid)) + parsed = code.title + except (Code.DoesNotExist, Series.DoesNotExist): + return JsonResponse({"error": "No code found for Series %s Code %s" % (seriesid, codeid)}) + + else: + parsed = ":".join(literal.split(":")[1:]) + + content = Content.objects.create(literal=literal) + media = Media.objects.create(content=content) + + output = { + "uuid": media.uuid, + "literal": media.content.literal + } + + if code: + output["parsed"] = parsed + output["code"] = model_to_dict(code) + output["code"]["series"] = model_to_dict(code.series) + + else: + output = {"error": "No code found in provided image"} + + return JsonResponse(output) \ No newline at end of file diff --git a/customsettings.dist.py b/customsettings.dist.py index 808027c..eedfd03 100644 --- a/customsettings.dist.py +++ b/customsettings.dist.py @@ -2,10 +2,13 @@ # See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'me98p&0-ijj-*vov@vm_&z&x#gr9uvvc9*y$n!!%=+javz^-#7' +SECRET_KEY = 'somesecretstring' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] +AWS_BUCKET = "bucket" +AWS_ACCESS_KEY_ID = "accesskey" +AWS_SECRET_ACCESS_KEY = "secretkey" diff --git a/doc/swagger.yaml b/doc/swagger.yaml index 67373e7..7a97b5d 100644 --- a/doc/swagger.yaml +++ b/doc/swagger.yaml @@ -322,6 +322,8 @@ paths: schema: url: type: string + path: + type: string uuid: type: string format: uuid diff --git a/reader/__init__.py b/reader/__init__.py index f1cbcfb..d6e44c5 100644 --- a/reader/__init__.py +++ b/reader/__init__.py @@ -11,14 +11,13 @@ class QRCode: self.content = content self.rect = rect -def read_code(imagepath): - image = PIL.Image.open(imagepath) +def read_code(image): reader = zxing.BarCodeReader() codes = [] for _ in range(10): - tempimage = tempfile.NamedTemporaryFile() - image.save(tempimage, format="png") + tempimage = tempfile.NamedTemporaryFile(suffix=".png") + image.write(tempimage.name) zxcontent = reader.decode(tempimage.name) zbcontent = pyzbar.pyzbar.decode(PIL.Image.open(tempimage.name)) content = [] @@ -30,13 +29,11 @@ def read_code(imagepath): content.append(single.data.decode()) for code in content: - if not code in codes: - codes.append(code) + if code.startswith("EXP360:"): + return code - if codes: - return codes - - image = PIL.ImageEnhance.Contrast(image).enhance(2.5) + image.modulate(150, 100, 100) + image.contrast(150) if __name__ == "__main__": content = read_code(sys.argv[1]) diff --git a/requirements.txt b/requirements.txt index ad5c60e..23645b1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,8 @@ django pyqrcode -PIL -numpy \ No newline at end of file +pillow +numpy +boto3 +pyzbar +numpy +zxing \ No newline at end of file