Passed
Push — master ( ad69f1...c2f5ad )
by Yu
02:04
created

ebook_homebrew.rest.list_upload_files()   A

Complexity

Conditions 1

Size

Total Lines 18
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

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