Commit b1c2f7da authored by Francois Ledoyen's avatar Francois Ledoyen
Browse files

Update services and queries

parent 2f6a93b5
Loading
Loading
Loading
Loading
+13 −10
Changes for tagthunder/api/routers/queries.py: 13 added lines, 10 removed lines.
Original line number Diff line number Diff line
from typing import Optional, Union

import fastapi
import pydantic

import api.configurations.pipeline as algos
import api.models.schemas as api_schemas

@@ -27,8 +30,8 @@ class AlgorithmQuery(BaseModel):
    pass


class AugmentationQuery(BaseModel):
    url: HttpUrl
class AugmentationQuery(AlgorithmQuery):
    url: HttpUrl = pydantic.Field(..., description="url", example="https://www.example.com")
    algorithm: Optional[Queries.AUGMENTATION] = DefaultQuery.AUGMENTATION


@@ -48,15 +51,15 @@ class ExtractionQuery(AlgorithmQuery):


class VocalizationQuery(AlgorithmQuery):
    keywords: api_schemas.Keywords
    segmentation: api_schemas.Segmentation
    algorithm: Optional[Queries.VOCALIZATION] = DefaultQuery.VOCALIZATION


class PipelineQuery(AlgorithmQuery):
    url: HttpUrl
    htmlpp: Optional[api_schemas.HTMLPP]
    recompute: bool = False
    cleaning: Optional[Queries.CLEANING] = DefaultQuery.CLEANING
    segmentation: Optional[Queries.SEGMENTATION] = DefaultQuery.SEGMENTATION
    extraction: Optional[Queries.EXTRACTION] = DefaultQuery.EXTRACTION
    vocalization: Optional[Queries.VOCALIZATION] = DefaultQuery.VOCALIZATION
    url: HttpUrl = pydantic.Field(..., description="url", example="https://www.example.com")
    htmlpp: Optional[api_schemas.HTMLPP] = pydantic.Field(..., description="HTML++", example="")
    augmentation: Optional[Queries.AUGMENTATION] = None
    cleaning: Optional[Queries.CLEANING] = None
    segmentation: Optional[Queries.SEGMENTATION] = None
    extraction: Optional[Queries.EXTRACTION] = None
    vocalization: Optional[Queries.VOCALIZATION] = None
+49 −6
Changes for tagthunder/api/routers/routes.py: 49 added lines, 6 removed lines.
Original line number Diff line number Diff line
import base64
import io
import typing

import fastapi
import pydantic

@@ -98,21 +102,60 @@ def extraction(
async def vocalization(
        query: queries.VocalizationQuery
):
    audio = services.AlgorithmServices.vocalization(
        query.keywords,
    return services.AlgorithmServices.vocalization(
        query.segmentation,
        query.algorithm.name,
        query.algorithm.parameters.dict()
    )

    return fastapi.responses.StreamingResponse(audio, media_type="audio/mpeg3")


@router.post(
    Routes.PIPELINE,
    description="Pipeline of TagThunder operations",
    response_model=schemas.Segmentation
    # response_class=typing.Union[
    #     fastapi.responses.StreamingResponse,
    #     fastapi.responses.JSONResponse
    # ],
    response_model=typing.Union[
        schemas.HTMLP,
        schemas.HTMLPP,
        schemas.Segmentation
    ]
)
def pipeline(
        query: queries.PipelineQuery
):
    raise NotImplementedError()
    res = None
    if query.augmentation is not None:
        res = services.AlgorithmServices.augmentation(
            query.url,
            query.augmentation.name,
            query.augmentation.parameters.dict()
        )
    if query.cleaning is not None:
        res = services.AlgorithmServices.cleaning(
            htmlp=res,
            algorithm_name=query.cleaning.name,
            parameters=query.cleaning.parameters.dict()
        )
    if query.segmentation is not None:
        res = services.AlgorithmServices.segmentation(
            htmlpp=res,
            algorithm_name=query.segmentation.name,
            parameters=query.segmentation.parameters.dict()
        )
    if query.extraction is not None:
        res = services.AlgorithmServices.extraction(
            segmentation=res,
            algorithm_name=query.extraction.name,
            parameters=query.extraction.parameters.dict()
        )

    if query.vocalization is not None:
        res = services.AlgorithmServices.vocalization(
            segmentation=res,
            algorithm_name=query.vocalization.name,
            parameters=query.vocalization.parameters.dict()
        )

    return res
+57 −17
Changes for tagthunder/api/routers/services.py: 57 added lines, 17 removed lines.
Original line number Diff line number Diff line
import csv
import enum
import io
import zipfile
from typing import Optional

import fastapi
import toolz

from pydantic import HttpUrl

import api.models.schemas as schemas
@@ -12,35 +18,69 @@ class AlgorithmServices:

    @classmethod
    def augmentation(cls, url: [HttpUrl], algorithm_name: str, parameters) -> Optional[schemas.HTMLP]:
        augmentation_block = algorithms_conf.AugmentationBlocks.get_algorithm(algorithm_name)

        htmlp = augmentation_block(url, **parameters)
        algorithm = cls.get_algorithm("augmentation", algorithm_name, **parameters)
        htmlp = algorithm(url)
        return factories.Responses.HTMLP(htmlp)

    @classmethod
    def cleaning(cls, htmlp: schemas.HTMLP, algorithm_name: str, parameters) -> schemas.HTMLPP:
        cleaning_algorithm = algorithms_conf.CleaningBlocks.get_algorithm(algorithm_name)
        html_cleaned = cleaning_algorithm(factories.AlgorithmInput.HTMLP(htmlp), **parameters)
        algorithm = cls.get_algorithm("cleaning", algorithm_name, **parameters)
        html_cleaned = algorithm(factories.AlgorithmInput.HTMLP(htmlp))
        return factories.Responses.HTMLPP(html_cleaned)

    @classmethod
    def segmentation(cls, htmlpp: schemas.HTMLPP, algorithm_name: str, parameters) -> schemas.Segmentation:
        segmentation_algorithm = algorithms_conf.SegmentationBlocks.get_algorithm(algorithm_name)
        segmentation = segmentation_algorithm(
            factories.AlgorithmInput.HTMLPP(htmlpp),
            **parameters
        )

        algorithm = cls.get_algorithm("segmentation", algorithm_name, **parameters)
        segmentation = algorithm(factories.AlgorithmInput.HTMLPP(htmlpp))
        return factories.Responses.Segmentation(segmentation)

    @classmethod
    def extraction(cls, segmentation: schemas.Segmentation, algorithm_name: str, parameters) -> schemas.Segmentation:
        extraction_algorithm = algorithms_conf.ExtractionBlocks.get_algorithm(algorithm_name)
        segmentation = extraction_algorithm(factories.AlgorithmInput.Segmentation(segmentation), **parameters)

        algorithm = cls.get_algorithm("extraction", algorithm_name, **parameters)
        segmentation = algorithm(factories.AlgorithmInput.Segmentation(segmentation))
        return factories.Responses.Segmentation(segmentation)

    @classmethod
    def vocalization(cls, keywords: schemas.Keywords, algorithm_name: str, parameters) -> io.BytesIO:
        vocalization_algorithm = algorithms_conf.VocalizationBlocks.get_algorithm(algorithm_name)
        return vocalization_algorithm(factories.AlgorithmInput.Keywords(keywords), **parameters)
    def vocalization(cls, segmentation: schemas.Segmentation, algorithm_name: str,
                     parameters) -> fastapi.responses.StreamingResponse:
        algorithm = cls.get_algorithm("vocalization", algorithm_name, **parameters)
        vocalization = algorithm(factories.AlgorithmInput.Segmentation(segmentation))
        # for f in vocalization:
        #     play_song(f)
        zip = MiscServices.zip_files(
            files=vocalization,
            file_name_func=lambda file: f"zone_{file.zone_id}.wav"
        )
        response = fastapi.responses.StreamingResponse(iter([zip.getvalue()]),
                                                       media_type="application/x-zip-compressed")
        response.headers["Content-Disposition"] = "attachment; filename=vocalization.zip"

        return response

    @classmethod
    def get_algorithm(cls, block_type, name, **parameters):
        algo = algorithms_conf.pipeline_blocks[block_type].get_algorithm(name)
        parameters = cls.prepare_parameters(parameters)
        algo.config(**parameters)
        return algo

    @classmethod
    def prepare_parameters(cls, parameters):
        def manage_enum(value):
            if isinstance(value, enum.Enum):
                return value.value
            return value

        return toolz.valmap(manage_enum, parameters)


class MiscServices:
    @classmethod
    def zip_files(cls, files, file_name_func):
        zipped_file = io.BytesIO()
        with zipfile.ZipFile(zipped_file, "a", zipfile.ZIP_DEFLATED) as zipped:
            for i, f in enumerate(files):
                f.seek(0)
                zipped.writestr(file_name_func(f), f.read())
        zipped_file.seek(0)
        return zipped_file