Passed
Push — master ( 584ffe...9da24d )
by Yu
01:40
created

ebook_homebrew.rest.Upload.__init__()   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 2
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
# -*- coding: utf-8 -*-
2 1
"""Provides Rest API interfaces
3
"""
4
5 1
import os
6 1
import base64
7 1
import glob
8 1
import json
9 1
import datetime
10 1
import tempfile
11 1
from collections import namedtuple
12 1
import responder
13 1
from marshmallow import Schema, fields, ValidationError
14
15 1
from .convert import Image2PDF
16 1
from .utils.logging import get_logger
17 1
from .models.models import UploadModel, ErrorModel, FileNotFoundModel
18 1
from .__init__ import __version__
19
20 1
api = responder.API(
21
    title="Ebook-homebrew",
22
    debug=True,
23
    version=__version__,
24
    static_dir=os.path.join(os.path.dirname(os.path.abspath(__file__)), "static"),
25
    static_route="/static",
26
    openapi="3.0.2",
27
    docs_route="/docs",
28
    openapi_route="/schema.yml",
29
    description="Make PDF file taken in "
30
    "some image files such as "
31
    "jpeg, png and gif.",
32
    contact={
33
        "name": "tubone24",
34
        "url": "https://tubone-project24.xyz",
35
        "email": "[email protected]",
36
    },
37
    license={"name": "MIT", "url": "https://opensource.org/licenses/MIT"},
38
)
39
40 1
_logger = get_logger("RestAPI")
41
42
43 1
@api.schema("HealthCheck")
44 1
class HealthCheckSchema(Schema):
45 1
    status = fields.Str()
46
47
48 1
@api.schema("UploadImagesReq")
49 1
class UploadImagesReqSchema(Schema):
50 1
    contentType = fields.Str(required=True)
51 1
    images = fields.List(fields.Str(required=True), required=True)
52
53
54 1
@api.schema("UploadIdResp")
55 1
class UploadIdRespSchema(Schema):
56 1
    upload_id = fields.Str()
57 1
    release_date = fields.Date()
58
59
60 1
@api.schema("ConvertReq")
61 1
class ConvertReqSchema(Schema):
62 1
    uploadId = fields.Str(required=True)
63 1
    contentType = fields.Str(required=True)
64
65
66 1
@api.schema("DownloadReq")
67 1
class DownloadReqSchema(Schema):
68 1
    uploadId = fields.Str(required=True)
69
70
71 1
@api.schema("ErrorResp")
72 1
class ErrorRespSchema(Schema):
73 1
    error = fields.Str()
74 1
    errorDate = fields.Date()
75
76
77 1
@api.schema("FileNotFoundResp")
78 1
class FileNotFoundRespSchema(Schema):
79 1
    reason = fields.Str()
80 1
    errorDate = fields.Date()
81
82
83 1
api.add_route("/", static=True)
84
85
86 1
@api.route("/status")
87
def status(_, resp):
88
    """Health Check Response.
89
    ---
90
    get:
91
        description: Get Status
92
        responses:
93
            "200":
94
                description: OK
95
                content:
96
                    application/json:
97
                        schema:
98
                            $ref: "#/components/schemas/HealthCheck"
99
    """
100 1
    _logger.debug("health Check")
101 1
    Status = namedtuple("Status", ["status"])
102 1
    resp.media = HealthCheckSchema().dump(Status("ok")).data
103
104
105 1
@api.route("/data/upload")
106
async def upload_image_file(req, resp):
107
    """Upload Image files.
108
    ---
109
    post:
110
        summary: Base64 encoded Images
111
112
        requestBody:
113
            description: base64 encoded Images in images Array
114
            content:
115
                application/json:
116
                    schema:
117
                        $ref: "#/components/schemas/UploadImagesReq"
118
        responses:
119
            "200":
120
                description: OK
121
                content:
122
                    application/json:
123
                        schema:
124
                            $ref: "#/components/schemas/UploadIdResp"
125
            "400":
126
                description: BadRequest
127
                content:
128
                    application/json:
129
                        schema:
130
                            $ref: "#/components/schemas/ErrorResp"
131
    """
132 1
    request = await req.media()
133 1
    try:
134 1
        data = UploadImagesReqSchema(strict=True).load(request).data
135 1
    except ValidationError as error:
136 1
        resp.status_code = api.status_codes.HTTP_400
137 1
        resp.media = ErrorRespSchema().dump(ErrorModel(error)).data
138 1
        return
139 1
    _logger.debug(data)
140 1
    content_type = data["contentType"]
141 1
    extension = convert_content_type_to_extension(content_type)
142 1
    images_b64 = data["images"]
143 1
    tmp_dir = tempfile.mkdtemp()
144 1
    write_image(images_b64, extension, tmp_dir)
145 1
    resp.media = UploadIdRespSchema().dump(UploadModel(tmp_dir)).data
146
147
148 1
@api.background.task
149
def write_image(images_b64, extension, tmp_dir):
150
    """Images write at tmp_dir
151
152
    This API is background task.
153
154
    Args:
155
     images_b64: Base64 encoded images list
156
     extension: Image extension
157
     tmp_dir: Temp directory writing images
158
    Returns:
159
        bool: If success return true.
160
161
    """
162 1
    for i, content in enumerate(images_b64):
163 1
        image = base64.b64decode(content)
164 1
        file_name = os.path.join(tmp_dir, str(i) + "." + extension)
165 1
        _logger.debug("file_name: {}".format(file_name))
166 1
        with open(file_name, "wb") as image_file:
167 1
            image_file.write(image)
168 1
    return True
169
170
171 1
@api.route("/convert/pdf")
172
async def convert_image_to_pdf(req, resp):
173
    """Convert Image files to PDF.
174
    ---
175
    post:
176
        summary: Upload Id witch get upload images and ContentType
177
178
        requestBody:
179
            description: Upload Id and ContentType
180
            content:
181
                application/json:
182
                    schema:
183
                        $ref: "#/components/schemas/ConvertReq"
184
        responses:
185
            "200":
186
                description: OK
187
                content:
188
                    application/json:
189
                        schema:
190
                            $ref: "#/components/schemas/UploadIdResp"
191
            "400":
192
                description: BadRequest
193
                content:
194
                    application/json:
195
                        schema:
196
                            $ref: "#/components/schemas/ErrorResp"
197
            "404":
198
                description: UploadIdNotFound
199
                content:
200
                    application/json:
201
                    application/json:
202
                        schema:
203
                            $ref: "#/components/schemas/FileNotFoundResp"
204
    """
205 1
    request = await req.media()
206 1
    try:
207 1
        data = ConvertReqSchema(strict=True).load(request).data
208 1
    except ValidationError as error:
209 1
        resp.status_code = api.status_codes.HTTP_400
210 1
        resp.media = ErrorRespSchema().dump(ErrorModel(error)).data
211 1
        return
212 1
    _logger.debug(data)
213 1
    upload_id = data["uploadId"]
214 1
    content_type = data["contentType"]
215 1
    if not os.path.isdir(upload_id):
216 1
        resp.status_code = api.status_codes.HTTP_404
217 1
        resp.media = (
218
            FileNotFoundRespSchema()
219
            .dump(FileNotFoundModel("upload_id is NotFound"))
220
            .data
221
        )
222 1
        return
223 1
    result_meta = os.path.join(upload_id, "result_meta.txt")
224 1
    if os.path.exists(result_meta):
225 1
        os.remove(result_meta)
226 1
    extension = convert_content_type_to_extension(content_type)
227 1
    file_list = sorted(
228
        glob.glob(os.path.join(upload_id, "*." + extension)), reverse=True
229
    )
230 1
    file_base, _ = os.path.splitext(os.path.basename(file_list[0]))
231 1
    digits = len(file_base)
232 1
    _logger.debug(file_list)
233 1
    convert_pdf(digits, extension, upload_id)
234 1
    resp.media = UploadIdRespSchema().dump(UploadModel(upload_id)).data
235
236
237 1
@api.background.task
238
def convert_pdf(digits, extension, upload_id):
239
    """Convert images to PDF
240
241
    This API is background task.
242
243
    Args:
244
     digits: file serial number digits
245
     extension: Image extension
246
     upload_id: Request ID
247
    Returns:
248
        bool: If success return true.
249
250
    """
251 1
    converter = Image2PDF(digits=digits, extension=extension, directory_path=upload_id)
252 1
    converter.make_pdf("result.pdf")
253 1
    with open(os.path.join(upload_id, "result_meta.txt"), "w") as result_txt:
254 1
        now = datetime.datetime.now()
255 1
        result = {
256
            "upload_id": upload_id,
257
            "digits": digits,
258
            "extension": extension,
259
            "datetime": now.strftime("%Y/%m/%d %H:%M:%S"),
260
        }
261 1
        result_txt.write(json.dumps(result))
262 1
    return True
263
264
265 1
@api.route("/convert/pdf/download")
266
async def download_result_pdf(req, resp):
267
    """Upload Image files.
268
    ---
269
    post:
270
        summary: Upload Id witch get upload images
271
272
        requestBody:
273
            description: Upload Id
274
            content:
275
                application/json:
276
                    schema:
277
                        $ref: "#/components/schemas/DownloadReq"
278
        responses:
279
            "200":
280
                description: OK
281
                content:
282
                    application/pdf:
283
                        schema:
284
                            type: string
285
                            format: binary
286
            "400":
287
                description: BadRequest
288
                content:
289
                    application/json:
290
                        schema:
291
                            $ref: "#/components/schemas/ErrorResp"
292
            "404":
293
                description: ResultFileNotFound
294
                content:
295
                    application/json:
296
                        schema:
297
                            $ref: "#/components/schemas/FileNotFoundResp"
298
    """
299 1
    request = await req.media()
300 1
    try:
301 1
        data = DownloadReqSchema(strict=True).load(request).data
302 1
    except ValidationError as error:
303 1
        resp.status_code = api.status_codes.HTTP_400
304 1
        resp.media = ErrorRespSchema().dump(ErrorModel(error)).data
305 1
        return
306 1
    upload_id = data["uploadId"]
307 1
    result_meta = os.path.join(upload_id, "result_meta.txt")
308 1
    if os.path.exists(result_meta):
309 1
        with open(os.path.join(upload_id, "result.pdf"), "rb") as result_pdf:
310 1
            resp.headers["Content-Type"] = "application/pdf"
311 1
            resp.content = result_pdf.read()
312
    else:
313 1
        resp.status_code = api.status_codes.HTTP_404
314 1
        resp.media = (
315
            FileNotFoundRespSchema().dump(FileNotFoundModel("resultFile NotFound")).data
316
        )
317
318
319 1
def convert_content_type_to_extension(content_type):
320
    """Convert image extension to Content-Type
321
322
    Args:
323
     content_type: Content-Type
324
    Returns:
325
        str: extension
326
327
    """
328 1
    if content_type == "image/jpeg":
329 1
        extension = "jpg"
330 1
    elif content_type == "image/png":
331 1
        extension = "png"
332 1
    elif content_type == "image/gif":
333 1
        extension = "gif"
334
    else:
335 1
        extension = False
336
337
    return extension
338