Another set of changes
This commit is contained in:
parent
ed97894891
commit
ff97a4845b
10 changed files with 791 additions and 11 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -2,4 +2,5 @@
|
|||
__pycache__/
|
||||
migrations/
|
||||
*.swp
|
||||
db.sqlite3
|
||||
db.sqlite3
|
||||
customsettings.py
|
13
core/management/commands/addapikey.py
Normal file
13
core/management/commands/addapikey.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
from django.core.management.base import BaseCommand, CommandError
|
||||
|
||||
from core.models import APIKey
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Add new API key'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('--description', "-d", required=False, type=str, default="")
|
||||
|
||||
def handle(self, *args, **options):
|
||||
key = APIKey.objects.create(description=options["description"])
|
||||
self.stdout.write(self.style.SUCCESS(f'Successfully created key "{ key.key }"'))
|
|
@ -1,4 +1,4 @@
|
|||
from django.db.models import Model, UUIDField, CharField, TextField, ForeignKey, CASCADE, PositiveIntegerField
|
||||
from django.db.models import Model, UUIDField, CharField, TextField, ForeignKey, CASCADE, PositiveIntegerField, SET_NULL
|
||||
|
||||
import uuid
|
||||
import string
|
||||
|
@ -72,13 +72,17 @@ class Code(Model):
|
|||
if not self.order:
|
||||
self.order = max([code.order for code in self.series.code_set.all()] + [0]) + 1
|
||||
|
||||
class Scan(Model):
|
||||
uuid = UUIDField()
|
||||
code = ForeignKey(Code, CASCADE)
|
||||
class Content(Model):
|
||||
literal = TextField()
|
||||
code = ForeignKey(Code, SET_NULL, null=True)
|
||||
|
||||
class Media(Model):
|
||||
uuid = UUIDField(primary_key=True)
|
||||
code = ForeignKey(Content, CASCADE)
|
||||
|
||||
@classmethod
|
||||
def generate_uuid(cls):
|
||||
uuid = uuid.uuid4(primary_key=True)
|
||||
id = uuid.uuid4()
|
||||
try:
|
||||
cls.objects.get(uuid=id)
|
||||
except cls.DoesNotExist:
|
||||
|
@ -88,4 +92,22 @@ class Scan(Model):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if not self.uuid:
|
||||
self.uuid = self.generate_uuid()
|
||||
self.uuid = self.generate_uuid()
|
||||
|
||||
class APIKey(Model):
|
||||
key = UUIDField(primary_key=True)
|
||||
description = CharField(max_length=256, blank=True, null=True)
|
||||
|
||||
@classmethod
|
||||
def generate_uuid(cls):
|
||||
id = uuid.uuid4()
|
||||
try:
|
||||
cls.objects.get(key=id)
|
||||
except cls.DoesNotExist:
|
||||
return id
|
||||
return cls.generate_uuid()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if not self.key:
|
||||
self.key = self.generate_uuid()
|
20
core/urls/__init__.py
Normal file
20
core/urls/__init__.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
"""expqr URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/3.1/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.urls import path, include
|
||||
|
||||
urlpatterns = [
|
||||
path('api/', include('core.urls.api'))
|
||||
]
|
27
core/urls/api.py
Normal file
27
core/urls/api.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
"""expqr URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/3.1/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.urls import path
|
||||
|
||||
from core.views import APICreateSeries, APISeriesByIDDispatcher, APICodesBySeriesIDDispatcher, APICreateCodes, APICodeByID
|
||||
|
||||
urlpatterns = [
|
||||
path('series/', APICreateSeries.as_view()),
|
||||
path('series/<id>/', APISeriesByIDDispatcher.as_view()),
|
||||
path('series/<id>/codes/', APICodesBySeriesIDDispatcher.as_view()),
|
||||
path('codes/', APICreateCodes.as_view()),
|
||||
path('codes/<uuid>/', APICodeByID.as_view()),
|
||||
path('codes/<seriesid>/<codeid>/', APICodeByID.as_view()),
|
||||
]
|
|
@ -1,3 +1,236 @@
|
|||
from django.views import View
|
||||
from django.http import JsonResponse
|
||||
from django.forms.models import model_to_dict
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.core.validators import URLValidator
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
|
||||
import urllib.request
|
||||
|
||||
from core.models import Series, APIKey, Code
|
||||
|
||||
class APIView(View):
|
||||
@method_decorator(csrf_exempt)
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
try:
|
||||
APIKey.objects.get(key=request.headers["apikey"])
|
||||
except APIKey.DoesNotExist:
|
||||
return JsonResponse({"error": "No valid API key provided"})
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
class JsonView(APIView):
|
||||
@method_decorator(csrf_exempt)
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
try:
|
||||
self.data = json.loads(request.body.decode("utf-8"))
|
||||
except Exception:
|
||||
return JsonResponse({"error": "No valid json found"})
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
class APICreateSeries(JsonView):
|
||||
def post(self, request, *args, **kwargs):
|
||||
try:
|
||||
title = self.data["title"]
|
||||
except KeyError:
|
||||
return JsonResponse({"error": "No title for Series provided"})
|
||||
|
||||
description = self.data.get("description")
|
||||
id = self.data.get("id")
|
||||
uuid = self.data.get("uuid")
|
||||
|
||||
if id:
|
||||
try:
|
||||
Series.objects.get(id=id)
|
||||
raise ValueError("Series object with id %s exists" % id)
|
||||
except Series.DoesNotExist:
|
||||
pass
|
||||
|
||||
if uuid:
|
||||
try:
|
||||
Series.objects.get(id=uuid)
|
||||
raise ValueError("Series object with uuid %s exists" % uuid)
|
||||
except Series.DoesNotExist:
|
||||
pass
|
||||
|
||||
series = Series.objects.create(uuid=uuid, id=id, title=title, description=description)
|
||||
|
||||
return JsonResponse(model_to_dict(series))
|
||||
|
||||
class SeriesAPIView(APIView):
|
||||
@method_decorator(csrf_exempt)
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
try:
|
||||
try:
|
||||
is_uuid = bool(uuid.UUID(kwargs["id"]))
|
||||
except:
|
||||
is_uuid = False
|
||||
|
||||
if is_uuid:
|
||||
self.series = Series.objects.get(uuid=kwargs["id"])
|
||||
else:
|
||||
self.series = Series.objects.get(id=kwargs["id"])
|
||||
|
||||
except Series.DoesNotExist:
|
||||
return JsonResponse({"error": "No Series with ID %s found" % kwargs["id"]})
|
||||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
class APISeriesByIDDispatcher(SeriesAPIView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
return JsonResponse(model_to_dict(self.series))
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
try:
|
||||
data = json.loads(request.body.decode("utf-8"))
|
||||
except Exception:
|
||||
return JsonResponse({"error": "No valid json found"})
|
||||
|
||||
title = data.get("title", self.series.title)
|
||||
description = data.get("description", self.series.description)
|
||||
id = data.get("id", self.series.id)
|
||||
uuid = data.get("uuid", self.series.uuid)
|
||||
|
||||
if id != self.series.id:
|
||||
return JsonResponse({"error": "Cannot change Series ID"})
|
||||
|
||||
if uuid != self.series.uuid:
|
||||
return JsonResponse({"error": "Cannot change Series UUID"})
|
||||
|
||||
self.series.title = title
|
||||
self.series.description = description
|
||||
self.series.save()
|
||||
|
||||
return JsonResponse(model_to_dict(self.series))
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
self.series.delete()
|
||||
return JsonResponse({})
|
||||
|
||||
class APICodesBySeriesIDDispatcher(SeriesAPIView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
return JsonResponse(list(self.series.code_set.values()), safe=False)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
try:
|
||||
data = json.loads(request.body.decode("utf-8"))
|
||||
except Exception:
|
||||
return JsonResponse({"error": "No valid json found"})
|
||||
|
||||
objects = []
|
||||
|
||||
for spec in data:
|
||||
title = spec["title"]
|
||||
id = spec.get("id")
|
||||
uuid = spec.get("uuid")
|
||||
order = 0
|
||||
try:
|
||||
code = Code(uuid=uuid, id=id, title=title, series=self.series, order=order)
|
||||
code.save()
|
||||
objects.append(model_to_dict(code))
|
||||
except Exception as e:
|
||||
for o in objects:
|
||||
o.delete()
|
||||
return JsonResponse({"error": "Failed creating Code objects: %s" % e})
|
||||
|
||||
return JsonResponse(objects, safe=False)
|
||||
|
||||
class APICreateCodes(APIView):
|
||||
def post(self, request, *args, **kwargs):
|
||||
try:
|
||||
data = json.loads(request.body.decode("utf-8"))
|
||||
except Exception:
|
||||
return JsonResponse({"error": "No valid json found"})
|
||||
|
||||
objects = []
|
||||
|
||||
for spec in data:
|
||||
title = spec["title"]
|
||||
id = spec.get("id")
|
||||
nuuid = spec.get("uuid")
|
||||
series_id = spec["series"]
|
||||
order = 0
|
||||
|
||||
try:
|
||||
is_uuid = bool(uuid.UUID(series_id))
|
||||
except:
|
||||
is_uuid = False
|
||||
|
||||
if is_uuid:
|
||||
series = Series.objects.get(uuid=series_id)
|
||||
else:
|
||||
series = Series.objects.get(id=series_id)
|
||||
|
||||
try:
|
||||
code = Code(uuid=nuuid, id=id, title=title, series=series, order=order)
|
||||
code.save()
|
||||
objects.append(model_to_dict(code))
|
||||
|
||||
except Exception as e:
|
||||
for o in objects:
|
||||
o.delete()
|
||||
return JsonResponse({"error": "Failed creating Code objects: %s" % e})
|
||||
|
||||
return JsonResponse(objects, safe=False)
|
||||
|
||||
class CodeAPIView(APIView):
|
||||
@method_decorator(csrf_exempt)
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
try:
|
||||
if kwargs.get("uuid"):
|
||||
self.code = Code.objects.get(uuid=kwargs.get("uuid"))
|
||||
else:
|
||||
self.code = Code.objects.get(id=kwargs["codeid"], series=Series.objects.get(id=kwargs["seriesid"]))
|
||||
|
||||
except Code.DoesNotExist:
|
||||
return JsonResponse({"error": "No Code for this definition found"})
|
||||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
class APICodeByID(CodeAPIView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
return JsonResponse(model_to_dict(self.code))
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
try:
|
||||
data = json.loads(request.body.decode("utf-8"))
|
||||
except Exception:
|
||||
return JsonResponse({"error": "No valid json found"})
|
||||
|
||||
title = data.get("title", self.code.title)
|
||||
id = data.get("id", self.code.id)
|
||||
uuid = data.get("uuid", self.code.uuid)
|
||||
|
||||
if id != self.code.id:
|
||||
return JsonResponse({"error": "Cannot change Code ID"})
|
||||
|
||||
if uuid != self.code.uuid:
|
||||
return JsonResponse({"error": "Cannot change Code UUID"})
|
||||
|
||||
self.code.title = title
|
||||
self.code.save()
|
||||
|
||||
return JsonResponse(model_to_dict(self.code))
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
self.code.delete()
|
||||
return JsonResponse({})
|
||||
|
||||
class APIAnalyzeContent(APIView):
|
||||
def post(self, request, *args, **kwargs):
|
||||
url = request.body.decode("utf-8")
|
||||
|
||||
try:
|
||||
val = URLValidator().val(url)
|
||||
except ValidationError:
|
||||
return JsonResponse({"error": "Failed to validate URL"})
|
||||
|
||||
content = BytesIO(urllib.request.urlopen('url').read())
|
||||
|
||||
|
|
@ -7,4 +7,5 @@ SECRET_KEY = 'me98p&0-ijj-*vov@vm_&z&x#gr9uvvc9*y$n!!%=+javz^-#7'
|
|||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
ALLOWED_HOSTS = []
|
||||
|
421
doc/swagger.yaml
Normal file
421
doc/swagger.yaml
Normal file
|
@ -0,0 +1,421 @@
|
|||
openapi: 3.0.0
|
||||
info:
|
||||
description: API for handling EXP360 QR Code generation and analysis
|
||||
version: "0.1"
|
||||
title: EXPQR
|
||||
contact:
|
||||
email: support@kumi.email
|
||||
tags:
|
||||
- name: series
|
||||
description: Operations on series of codes to be used in a single shoot
|
||||
- name: code
|
||||
description: Operations on individual QR codes
|
||||
- name: content
|
||||
description: Operations on content elements including QR codes
|
||||
paths:
|
||||
/series:
|
||||
post:
|
||||
tags:
|
||||
- series
|
||||
summary: Create a new code series
|
||||
description: ""
|
||||
operationId: addSeries
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Series"
|
||||
description: "(Partial) `Series` object to be created, required field: `title`"
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Series"
|
||||
"/series/{id}":
|
||||
get:
|
||||
tags:
|
||||
- series
|
||||
summary: Find Series by ID or UUID
|
||||
description: Returns a single Series object
|
||||
operationId: getSeriesByID
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
description: ID or UUID of series to return
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Series"
|
||||
"400":
|
||||
description: Invalid ID supplied
|
||||
"404":
|
||||
description: Series not found
|
||||
post:
|
||||
tags:
|
||||
- series
|
||||
summary: Modifies an existing Series object
|
||||
description: ""
|
||||
operationId: modifySeriesByID
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
description: ID or UUID of `Series` object to be modified
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Series"
|
||||
description: "Partial `Series` object, allowed fields: `title`, `description`"
|
||||
required: true
|
||||
responses:
|
||||
"405":
|
||||
description: Invalid input
|
||||
delete:
|
||||
tags:
|
||||
- series
|
||||
summary: Deletes a Series object
|
||||
description: ""
|
||||
operationId: deleteSeries
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
description: ID or UUID of `Series` object to delete
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"400":
|
||||
description: Invalid ID supplied
|
||||
"404":
|
||||
description: Series not found
|
||||
"/series/{id}/codes":
|
||||
get:
|
||||
tags:
|
||||
- series
|
||||
summary: Return all QR codes associated with Series
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
description: ID or UUID of `Series` object to return codes for
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
200:
|
||||
description: List of `Code` objects associated with `Series`
|
||||
post:
|
||||
tags:
|
||||
- series
|
||||
summary: creates new QR codes for Series
|
||||
description: ""
|
||||
operationId: createCodes
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
description: ID or UUID of the `Series` object to create codes for
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Code"
|
||||
description: (Partial) `Code` objects to be created, forbidden parameter: `series`
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Code"
|
||||
|
||||
"/code/":
|
||||
post:
|
||||
tags:
|
||||
- code
|
||||
summary: create new Code objects
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Code"
|
||||
responses:
|
||||
"200":
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Code"
|
||||
|
||||
"/code/{uuid}":
|
||||
get:
|
||||
tags:
|
||||
- code
|
||||
summary: returns a Code object by its UUID
|
||||
operationId: getCodeByUUID
|
||||
parameters:
|
||||
- name: uuid
|
||||
in: path
|
||||
description: UUID of the `Code` object to return
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
"200":
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Code"
|
||||
delete:
|
||||
tags:
|
||||
- code
|
||||
summary: delete a Code object by its UUID
|
||||
operationId: deleteCodeByUUID
|
||||
parameters:
|
||||
- name: uuid
|
||||
in: path
|
||||
description: UUID of the `Code` object to delete
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
"200":
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Code"
|
||||
post:
|
||||
tags:
|
||||
- code
|
||||
summary: modify a Code object by its UUID
|
||||
operationId: modifyCodeByUUID
|
||||
parameters:
|
||||
- name: uuid
|
||||
in: path
|
||||
description: UUID of the `Code` object to modify
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
requestBody:
|
||||
$ref: "#/components/requestBodies/modifyCodeByUUIDBody"
|
||||
responses:
|
||||
"200":
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Code"
|
||||
"/code/{seriesid}/{codeid}":
|
||||
get:
|
||||
tags:
|
||||
- code
|
||||
summary: returns a Code object by its Series ID and Code ID
|
||||
operationId: getCodeByID
|
||||
parameters:
|
||||
- name: seriesid
|
||||
in: path
|
||||
description: ID or UUID of the `Series` the `Code` object to return is part of
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: codeid
|
||||
in: path
|
||||
description: ID of the `Code` object to return
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Code"
|
||||
delete:
|
||||
tags:
|
||||
- code
|
||||
summary: delete a Code object by its Series ID and Code ID
|
||||
operationId: deleteCodeByID
|
||||
parameters:
|
||||
- name: seriesid
|
||||
in: path
|
||||
description: ID or UUID of the `Series` the `Code` object to delete is part of
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: codeid
|
||||
in: path
|
||||
description: ID of the `Code` object to delete
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Code"
|
||||
post:
|
||||
tags:
|
||||
- code
|
||||
summary: modify a Code object by its Series ID and Code ID
|
||||
operationId: modifyCodeByID
|
||||
parameters:
|
||||
- name: seriesid
|
||||
in: path
|
||||
description: ID or UUID of the `Series` the `Code` object to modify is part of
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: codeid
|
||||
in: path
|
||||
description: ID of the `Code` object to modify
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
$ref: "#/components/requestBodies/modifyCodeByUUIDBody"
|
||||
responses:
|
||||
"200":
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Code"
|
||||
/content:
|
||||
post:
|
||||
tags:
|
||||
- content
|
||||
summary: analyze new media item and return content
|
||||
operationId: createContent
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
url:
|
||||
type: string
|
||||
uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Media"
|
||||
"/content/{uuid}":
|
||||
get:
|
||||
tags:
|
||||
- content
|
||||
summary: fetch details on media item by UUID
|
||||
parameters:
|
||||
- name: uuid
|
||||
in: path
|
||||
description: UUID of the `Media` item to retrieve
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Media"
|
||||
security:
|
||||
- api_key: []
|
||||
servers:
|
||||
- url: https://expqr.kumi.live/api
|
||||
components:
|
||||
requestBodies:
|
||||
modifyCodeByUUIDBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: string
|
||||
description: New title of the `Code` object
|
||||
required: true
|
||||
securitySchemes:
|
||||
api_key:
|
||||
type: apiKey
|
||||
name: api_key
|
||||
in: header
|
||||
schemas:
|
||||
Series:
|
||||
type: object
|
||||
required:
|
||||
- title
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
Code:
|
||||
type: object
|
||||
required:
|
||||
- title
|
||||
properties:
|
||||
title:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
series:
|
||||
$ref: "#/components/schemas/Series"
|
||||
Media:
|
||||
type: object
|
||||
required:
|
||||
- uuid
|
||||
properties:
|
||||
uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
content:
|
||||
$ref: "#/components/schemas/Content"
|
||||
Content:
|
||||
type: object
|
||||
required:
|
||||
- literal
|
||||
properties:
|
||||
literal:
|
||||
type: string
|
||||
code:
|
||||
$ref: "#/components/schemas/Code"
|
||||
|
|
@ -13,9 +13,8 @@ Including another URLconf
|
|||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from django.urls import path, include
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('', include('core.urls')),
|
||||
]
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import zxing
|
||||
import pyzbar.pyzbar
|
||||
import PIL.Image
|
||||
import PIL.ImageEnhance
|
||||
import PIL.ImageFilter
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
class QRCode:
|
||||
def __init__(self, content, rect):
|
||||
self.content = content
|
||||
self.rect = rect
|
||||
|
||||
def read_code(imagepath):
|
||||
image = PIL.Image.open(imagepath)
|
||||
reader = zxing.BarCodeReader()
|
||||
codes = []
|
||||
|
||||
for _ in range(10):
|
||||
tempimage = tempfile.NamedTemporaryFile()
|
||||
image.save(tempimage, format="png")
|
||||
zxcontent = reader.decode(tempimage.name)
|
||||
zbcontent = pyzbar.pyzbar.decode(PIL.Image.open(tempimage.name))
|
||||
content = []
|
||||
|
||||
if zxcontent:
|
||||
content.append(zxcontent.raw)
|
||||
|
||||
for single in zbcontent:
|
||||
content.append(single.data.decode())
|
||||
|
||||
for code in content:
|
||||
if not code in codes:
|
||||
codes.append(code)
|
||||
|
||||
if codes:
|
||||
return codes
|
||||
|
||||
image = PIL.ImageEnhance.Contrast(image).enhance(2.5)
|
||||
|
||||
if __name__ == "__main__":
|
||||
content = read_code(sys.argv[1])
|
||||
print(content)
|
Loading…
Reference in a new issue