<commit_before>from ac_mediator.exceptions import *
from services.acservice.constants import *
from services.acservice.utils import *
from services.acservice.base import BaseACService
from services.acservice.auth import ACServiceAuthMixin
from services.acservice.search import ACServiceTextSearchMixin, translates_field, translates_filter_for_field
from services.acservice.download import ACDownloadMixin
from accounts.models import Account
import datetime
from django.utils import timezone


class FreesoundService(BaseACService, ACServiceAuthMixin, ACServiceTextSearchMixin, ACDownloadMixin):

    # General settings
    NAME = 'Freesound'
    URL = 'https://freesound.org'
    API_BASE_URL = "https://freesound.org/apiv2/"

    # Base
    def validate_response_status_code(self, response):
        if self.TEXT_SEARCH_ENDPOINT_URL in response.request.url:
            # If request was made to search endpoint, translate 404 to 'page not found exception'
            if response.status_code == 404:
                raise ACAPIPageNotFound
        if 'download/link' in response.request.url:
            # If request was made to download link endpoint, translate 404 to 'resource does not exist'
            if response.status_code == 404:
                raise ACAPIResourceDoesNotExist
        if response.status_code != 200:
            raise ACException(response.json()['detail'], response.status_code)
        return response.json()

    # Auth
    SUPPORTED_AUTH_METHODS = [APIKEY_AUTH_METHOD, ENDUSER_AUTH_METHOD]
    BASE_AUTHORIZE_URL = API_BASE_URL + 'oauth2/authorize/?client_id={0}&response_type=code'
    ACCESS_TOKEN_URL = API_BASE_URL + 'oauth2/access_token/'
    REFRESH_TOKEN_URL = API_BASE_URL + 'oauth2/refresh_token/'

    def get_auth_info_for_request(self, auth_method, account=None):
        header_content = 'Token {0}'.format(self.get_apikey())
        if auth_method == ENDUSER_AUTH_METHOD:
            header_content = 'Bearer {0}'.format(self.get_enduser_token(account))
        return {'headers': {'Authorization': header_content}}

    def get_access_token_from_credentials(self, credentials):
        return credentials.credentials['access_token']

    def get_refresh_token_from_credentials(self, credentials):
        return credentials.credentials['refresh_token']

    def check_credentials_are_valid(self, credentials):
        date_expired = credentials.modified + datetime.timedelta(seconds=credentials.credentials['expires_in'])
        if timezone.now() > date_expired:
            return False
        return True

    def check_credentials_should_be_renewed_background(self, credentials):
        # Currently, Freesound refresh tokens never expire, therefore credentials never need to be renewed in
        # the background. An expired access token will be automatically renewed at request time.
        return False

    # Search
    TEXT_SEARCH_ENDPOINT_URL = API_BASE_URL + 'search/text/'

    # Implement fields mapping

    @property
    def direct_fields_mapping(self):
        return {
            FIELD_URL: 'url',
            FIELD_NAME: 'name',
            FIELD_AUTHOR_NAME: 'username',
            FIELD_TAGS: 'tags',
            FIELD_DURATION: 'duration',
            FIELD_FILESIZE: 'filesize',
            FIELD_CHANNELS: 'channels',
            FIELD_BITRATE: 'bitrate',
            FIELD_BITDEPTH: 'bitdepth',
            FIELD_SAMPLERATE: 'samplerate',
            FIELD_FORMAT: 'type',
            FIELD_COLLECTION_URL: 'pack',
            FIELD_DESCRIPTION: 'description',
            FIELD_LICENSE_DEED_URL: 'license',
        }

    @translates_field(FIELD_LICENSE)
    def translate_field_license(self, result):
        return translate_cc_license_url(result['license'])

    @translates_field(FIELD_PREVIEW)
    def translate_field_preview(self, result):
        return result['previews']['preview-hq-ogg']

    @translates_field(FIELD_IMAGE)
    def translate_field_image(self, result):
        return result['images']['waveform_m']

    @translates_field(FIELD_AUTHOR_URL)
    def translate_field_author_url(self, result):
        return self.API_BASE_URL + 'users/{0}/'.format(result['username'])

    @translates_field(FIELD_TIMESTAMP)
    def translate_field_timestamp(self, result):
        return datetime.datetime.strptime(result['created'].split('.')[0], '%Y-%m-%dT%H:%M:%S')\
            .strftime(AUDIOCOMMONS_STRING_TIME_FORMAT)

    # Implement filters mapping

    @property
    def direct_filters_mapping(self):
        return {
            FIELD_CHANNELS: 'channels',
            FIELD_AUTHOR_NAME: 'username',
            FIELD_DURATION: 'duration',
            FIELD_FILESIZE: 'filesize',
            FIELD_BITRATE: 'bitrate',
            FIELD_BITDEPTH: 'bitdepth',
            FIELD_SAMPLERATE: 'samplerate',
            FIELD_TAG: 'tag',
        }

    @translates_filter_for_field(FIELD_FORMAT)
    def translate_filter_format(self, value):
        if value not in ['wav', 'mp3', 'ogg', 'flac', 'aiff']:
            raise ACFilterParsingException(
                'The provided value for filter \'{0}\' is not supported'.format(FIELD_FORMAT))
        return 'type', value

    @translates_filter_for_field(FIELD_LICENSE)<commit_msg>Add support for filter by id in Freesound service<commit_after>from ac_mediator.exceptions import *
from services.acservice.constants import *
from services.acservice.utils import *
from services.acservice.base import BaseACService
from services.acservice.auth import ACServiceAuthMixin
from services.acservice.search import ACServiceTextSearchMixin, translates_field, translates_filter_for_field
from services.acservice.download import ACDownloadMixin
from accounts.models import Account
import datetime
from django.utils import timezone


class FreesoundService(BaseACService, ACServiceAuthMixin, ACServiceTextSearchMixin, ACDownloadMixin):

    # General settings
    NAME = 'Freesound'
    URL = 'https://freesound.org'
    API_BASE_URL = "https://freesound.org/apiv2/"

    # Base
    def validate_response_status_code(self, response):
        if self.TEXT_SEARCH_ENDPOINT_URL in response.request.url:
            # If request was made to search endpoint, translate 404 to 'page not found exception'
            if response.status_code == 404:
                raise ACAPIPageNotFound
        if 'download/link' in response.request.url:
            # If request was made to download link endpoint, translate 404 to 'resource does not exist'
            if response.status_code == 404:
                raise ACAPIResourceDoesNotExist
        if response.status_code != 200:
            raise ACException(response.json()['detail'], response.status_code)
        return response.json()

    # Auth
    SUPPORTED_AUTH_METHODS = [APIKEY_AUTH_METHOD, ENDUSER_AUTH_METHOD]
    BASE_AUTHORIZE_URL = API_BASE_URL + 'oauth2/authorize/?client_id={0}&response_type=code'
    ACCESS_TOKEN_URL = API_BASE_URL + 'oauth2/access_token/'
    REFRESH_TOKEN_URL = API_BASE_URL + 'oauth2/refresh_token/'

    def get_auth_info_for_request(self, auth_method, account=None):
        header_content = 'Token {0}'.format(self.get_apikey())
        if auth_method == ENDUSER_AUTH_METHOD:
            header_content = 'Bearer {0}'.format(self.get_enduser_token(account))
        return {'headers': {'Authorization': header_content}}

    def get_access_token_from_credentials(self, credentials):
        return credentials.credentials['access_token']

    def get_refresh_token_from_credentials(self, credentials):
        return credentials.credentials['refresh_token']

    def check_credentials_are_valid(self, credentials):
        date_expired = credentials.modified + datetime.timedelta(seconds=credentials.credentials['expires_in'])
        if timezone.now() > date_expired:
            return False
        return True

    def check_credentials_should_be_renewed_background(self, credentials):
        # Currently, Freesound refresh tokens never expire, therefore credentials never need to be renewed in
        # the background. An expired access token will be automatically renewed at request time.
        return False

    # Search
    TEXT_SEARCH_ENDPOINT_URL = API_BASE_URL + 'search/text/'

    # Implement fields mapping

    @property
    def direct_fields_mapping(self):
        return {
            FIELD_URL: 'url',
            FIELD_NAME: 'name',
            FIELD_AUTHOR_NAME: 'username',
            FIELD_TAGS: 'tags',
            FIELD_DURATION: 'duration',
            FIELD_FILESIZE: 'filesize',
            FIELD_CHANNELS: 'channels',
            FIELD_BITRATE: 'bitrate',
            FIELD_BITDEPTH: 'bitdepth',
            FIELD_SAMPLERATE: 'samplerate',
            FIELD_FORMAT: 'type',
            FIELD_COLLECTION_URL: 'pack',
            FIELD_DESCRIPTION: 'description',
            FIELD_LICENSE_DEED_URL: 'license',
        }

    @translates_field(FIELD_LICENSE)
    def translate_field_license(self, result):
        return translate_cc_license_url(result['license'])

    @translates_field(FIELD_PREVIEW)
    def translate_field_preview(self, result):
        return result['previews']['preview-hq-ogg']

    @translates_field(FIELD_IMAGE)
    def translate_field_image(self, result):
        return result['images']['waveform_m']

    @translates_field(FIELD_AUTHOR_URL)
    def translate_field_author_url(self, result):
        return self.API_BASE_URL + 'users/{0}/'.format(result['username'])

    @translates_field(FIELD_TIMESTAMP)
    def translate_field_timestamp(self, result):
        return datetime.datetime.strptime(result['created'].split('.')[0], '%Y-%m-%dT%H:%M:%S')\
            .strftime(AUDIOCOMMONS_STRING_TIME_FORMAT)

    # Implement filters mapping

    @property
    def direct_filters_mapping(self):
        return {
            FIELD_CHANNELS: 'channels',
            FIELD_AUTHOR_NAME: 'username',
            FIELD_DURATION: 'duration',
            FIELD_FILESIZE: 'filesize',
            FIELD_BITRATE: 'bitrate',
            FIELD_BITDEPTH: 'bitdepth',
            FIELD_SAMPLERATE: 'samplerate',
            FIELD_TAG: 'tag',
        }

    @translates_filter_for_field(FIELD_ID)
    def translate_filter_id(self, value):
        """
        Implementation for the translation of ID filter. It uses the provided SERVICE_ID_FIELDNAME as key
        for the filter, and removes self.id_prefix from the start of the filter value. This should operate in reverse
        to what `translate_field_id`. In `translate_field_id` we append a prefix to the third party service provided
        id (e.g. 1234 -> prefix:1234). Here we remove that prefix to convert the value to the original (e.g. prefix:1234
        -> 1234).
        """
        return self.SERVICE_ID_FIELDNAME, value[len(self.id_prefix):]

    @translates_filter_for_field(FIELD_FORMAT)
    def translate_filter_format(self, value):
        if value not in ['wav', 'mp3', 'ogg', 'flac', 'aiff']:
            raise ACFilterParsingException(
                'The provided value for filter \'{0}\' is not supported'.format(FIELD_FORMAT))
        return 'type', value

    @translates_filter_for_field(FIELD_LICENSE)
{'fraction_non_alphanumeric': 0.08958707360861759, 'fraction_numerical': 0.006104129263913824, 'mean_word_length': 3.2474266107510483, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '948586', 'n_tokens_mistral': 3303, 'n_tokens_neox': 3142, 'n_words': 714}