Exemplo n.º 1
0
    def walk_dir(self, base, ignore_index=False, prefix=''):
        from inflector import Inflector
        from django.utils.module_loading import import_string
        from drf_auto_endpoint.app_settings import settings as auto_settings

        inflector_language = import_string(auto_settings.INFLECTOR_LANGUAGE)
        inflector = Inflector(inflector_language)

        imports = []

        for item in os.listdir(base):
            filename = os.path.join(base, item)
            if os.path.isdir(filename):
                imports += self.walk_dir(filename,
                                         prefix=os.path.join(
                                             prefix, item.replace('_', '-')))
            elif item == 'index.js' and ignore_index:
                continue
            else:
                try:
                    base_name, extension = item.rsplit('.', 1)
                except ValueError:
                    # a file without extension
                    continue
                if extension != 'js':
                    continue
                imports.append(
                    (os.path.join(prefix, inflector.pluralize(base_name)),
                     os.path.join(prefix,
                                  base_name).replace('/',
                                                     '_').replace('-', '_'),
                     os.path.join('.', prefix.replace('-', '_'), base_name)))
        return imports
Exemplo n.º 2
0
def get_search_results(query, exact_match=False):
    query = join_bigrams.bigramar(query.lower(), bigrams)
    query = [query] if exact_match else query.split()
    results = [[], [], []]
    for row in data:
        query_match_level = 0
        for query_word in query:
            search_list = [query_word] if exact_match else [
                query_word,
                Inflector.pluralize(query_word),
                Inflector.singularize(query_word)
            ]
            word_match_level = consts.NOT_FOUND
            for word in search_list:
                word_match_level = min(word_match_level,
                                       get_match_level(word, row))
            query_match_level = max(query_match_level, word_match_level)
        if query_match_level != consts.NOT_FOUND:
            results[query_match_level].append(copy.copy(row))
    merged_results = results[0] + results[1] + results[2]
    for result in merged_results:
        for level in range(3):
            for column in consts.SEARCH_COLUMNS[level]:
                result.pop(column + '_processed')
    return json.dumps(merged_results)
Exemplo n.º 3
0
def wizard(target_model, serializer, icon_class=None, btn_class=None, text=None, **kwargs):

    inflector_language = import_string(settings.INFLECTOR_LANGUAGE)
    inflector = Inflector(inflector_language)

    _kwargs = {
        'type': 'wizard',
        'params': {},
    }
    _kwargs.update(kwargs)
    kwargs = _kwargs

    fieldsets = kwargs.pop('fieldsets', None)
    if fieldsets is None:
        fieldsets = [
            {'name': field}
            for field in serializer.Meta.fields
        ]
    kwargs['params']['fieldsets'] = fieldsets

    serializer_instance = serializer()
    needs = []
    fields = []
    Adapter = import_string(settings.METADATA_ADAPTER)
    for field in serializer.Meta.fields:
        field_instance = serializer_instance.fields[field]
        if isinstance(field_instance, PrimaryKeyRelatedField):
            model = field_instance.queryset.model
            needs.append({
                'app': model._meta.app_label,
                'singular': model._meta.model_name.lower(),
                'plural': inflector.pluralize(model._meta.model_name.lower()),
            })
        fields.append(Adapter.adapt_field(get_field_dict(field, serializer)))
    kwargs['params']['needs'] = needs
    kwargs['params']['fields'] = fields
    kwargs['languages'] = get_languages()

    def decorator(func):
        # cls = getattr(inspect.getmodule(func),
        #               func.__qualname__.split('.<locals>', 1)[0].rsplit('.', 1)[0])
        func.bind_to_methods = [kwargs.pop('method', 'POST'), ]
        func.detail = True
        func.action_type = 'custom'
        func.action_kwargs = action_kwargs(icon_class, btn_class, text, func, kwargs)
        func.kwargs = {}
        func.action_kwargs['params']['model'] = '{}/{}/{}'.format(
            target_model._meta.app_label.lower(),
            inflector.pluralize(target_model._meta.model_name.lower()),
            func.__name__
        )
        func.serializer = serializer

        return Adapter.adapt_wizard(func)

    return decorator
Exemplo n.º 4
0
class EnglishInflectorTestCase(unittest.TestCase):
    singular_to_plural = {
        "search"      : "searches",
        "switch"      : "switches",
        "fix"         : "fixes",
        "box"         : "boxes",
        "process"     : "processes",
        "address"     : "addresses",
        "case"        : "cases",
        "stack"       : "stacks",
        "wish"        : "wishes",
        "fish"        : "fish",
    
        "category"    : "categories",
        "query"       : "queries",
        "ability"     : "abilities",
        "agency"      : "agencies",
        "movie"       : "movies",
    
        "archive"     : "archives",
    
        "index"       : "indices",
    
        "wife"        : "wives",
        "safe"        : "saves",
        "half"        : "halves",
    
        "move"        : "moves",
    
        "salesperson" : "salespeople",
        "person"      : "people",
    
        "spokesman"   : "spokesmen",
        "man"         : "men",
        "woman"       : "women",
    
        "basis"       : "bases",
        "diagnosis"   : "diagnoses",
    
        "datum"       : "data",
        "medium"      : "media",
        "analysis"    : "analyses",
    
        "node_child"  : "node_children",
        "child"       : "children",
    
        "experience"  : "experiences",
        "day"         : "days",
    
        "comment"     : "comments",
        "foobar"      : "foobars",
        "newsletter"  : "newsletters",
    
        "old_news"    : "old_news",
        "news"        : "news",
    
        "series"      : "series",
        "species"     : "species",
    
        "quiz"        : "quizzes",
    
        "perspective" : "perspectives",
    
        "ox" : "oxen",
        "photo" : "photos",
        "buffalo" : "buffaloes",
        "tomato" : "tomatoes",
        "dwarf" : "dwarves",
        "elf" : "elves",
        "information" : "information",
        "equipment" : "equipment",
        "bus" : "buses",
        "status" : "statuses",
        "mouse" : "mice",
    
        "louse" : "lice",
        "house" : "houses",
        "octopus" : "octopi",
        "virus" : "viri",
        "alias" : "aliases",
        "portfolio" : "portfolios",
    
        "vertex" : "vertices",
        "matrix" : "matrices",
    
        "axis" : "axes",
        "testis" : "testes",
        "crisis" : "crises",
    
        "rice" : "rice",
        "shoe" : "shoes",
    
        "horse" : "horses",
        "prize" : "prizes",
        "edge" : "edges"
    }
    def setUp(self):
        self.inflector = Inflector(English)
    
    def tearDown(self):
        self.inflector = None

    def test_pluralize(self) :
        for singular in self.singular_to_plural.keys() :
            assert self.inflector.pluralize(singular) == self.singular_to_plural[singular], \
            'English Inlector pluralize(%s) should produce "%s" and NOT "%s"' % (singular, self.singular_to_plural[singular], self.inflector.pluralize(singular))
            
    def test_singularize(self) :
        for singular in self.singular_to_plural.keys() :
            assert self.inflector.singularize(self.singular_to_plural[singular]) == singular, \
            'English Inlector singularize(%s) should produce "%s" and NOT "%s"' % (self.singular_to_plural[singular], singular, self.inflector.singularize(self.singular_to_plural[singular]))
Exemplo n.º 5
0
class Endpoint(object):

    base_serializer = import_string(settings.BASE_SERIALIZER)
    base_viewset = import_string(settings.BASE_VIEWSET)
    base_readonly_viewset = import_string(settings.BASE_READONLY_VIEWSET)

    model = None
    fields = None
    serializer = None

    fieldsets = None
    list_display = None
    list_editable = None
    extra_fields = None

    permission_classes = None
    filter_fields = None
    search_fields = None
    ordering_fields = None
    page_size = None
    viewset = None

    read_only = False
    include_str = True
    list_me = True

    save_twice = False
    sortable_by = None

    custom_actions = None
    bulk_actions = None

    inflector_language = import_string(settings.INFLECTOR_LANGUAGE)

    _translated_fields = None
    _translated_field_names = None
    _default_language_field_names = None

    def __init__(self, model=None, **kwargs):
        self.inflector = Inflector(self.inflector_language)

        if model is not None:
            self.model = model

        arg_names = ('fields', 'serializer', 'permission_classes',
                     'filter_fields', 'search_fields', 'viewset', 'read_only',
                     'include_str', 'ordering_fields', 'page_size',
                     'base_viewset', 'fields_annotation', 'fieldsets',
                     'base_serializer', 'list_me')
        for arg_name in arg_names:
            setattr(self, arg_name,
                    kwargs.pop(arg_name, getattr(self, arg_name, None)))

        if len(kwargs.keys()) > 0:
            raise Exception(
                '{} got an unexpected keyword argument: "{}"'.format(
                    self.__class__.__name__,
                    list(kwargs.keys())[0]))

        if self.serializer is not None:
            assert self.fields is None, 'You cannot specify both fields and serializer'
        else:
            assert self.viewset is not None or self.model is not None, \
                'You need to specify at least a model or a viewset'
            self.get_serializer()

        if self.viewset is not None:
            for attr in ('permission_classes', 'filter_fields',
                         'search_fields', 'ordering_fields', 'page_size'):
                assert getattr(self, attr, None) is None, \
                    'You cannot specify both {} and viewset'.format(attr)
        else:
            self.get_viewset()

        if self.model is None:
            self.model = self.get_serializer().Meta.model

    def get_languages(self):
        return get_languages()

    @property
    def singular_model_name(self):
        return self.model._meta.model_name.lower()

    @property
    def model_name(self):
        return self.inflector.pluralize(self.singular_model_name)

    @property
    def application_name(self):
        return self.model._meta.app_label.lower()

    def get_fields_for_serializer(self):

        if self.fields is None:
            self.fields = tuple([
                f for f in get_all_field_names(self.model)
                if f not in self.default_language_field_names
            ])
            if self.extra_fields is not None:
                self.fields += tuple(self.extra_fields)
            if self.include_str:
                self.fields += ('__str__', )

        return self.fields

    def get_serializer(self, data=None):

        if self.serializer is None:
            if self.viewset is None:
                self.serializer = serializer_factory(self)
            else:
                self.serializer = self.viewset.serializer_class

        if data is None:
            return self.serializer

        return self.serializer(data)

    def get_base_viewset(self):
        return self.base_viewset if not self.read_only or self.base_viewset != viewsets.ModelViewSet \
            else self.base_readonly_viewset

    def get_viewset(self):

        if self.viewset is None:
            self.viewset = viewset_factory(self)

        return self.viewset

    def get_url(self):

        return '{}/{}'.format(self.application_name.replace('_', '-'),
                              self.model_name.replace('_', '-'))

    def _get_field_dict(self, field):
        return get_field_dict(field, self.get_serializer(),
                              self.get_translated_fields(),
                              self.fields_annotation, self.model)

    def get_fields(self):
        return [
            self._get_field_dict(field)
            for field in self.get_fields_for_serializer()
        ]

    def get_fieldsets(self):
        if self.fieldsets is not None:
            return [{
                'key': field
            } if not isinstance(field, dict) else field
                    for field in self.fieldsets]

        return [{
            'key': field
        } for field in self.get_fields_for_serializer()
                if field != 'id' and field != '__str__'
                and field not in self.translated_field_names
                and self._get_field_dict(field)['type'][:6] != 'tomany']

    def get_list_display(self):
        if self.list_display is None:
            if '__str__' in self.get_fields_for_serializer():
                return [
                    '__str__',
                ]
            return [self.get_fields()[0]['key']]
        return self.list_display

    def _get_endpoint_list(self, name):
        value = getattr(self, name, None)
        if value is None:
            return []
        return value

    def get_filter_fields(self):
        fields = self._get_endpoint_list('filter_fields')
        return fields

    @property
    def search_enabled(self):
        fields = self._get_endpoint_list('search_fields')
        return len(fields) > 0

    def get_ordering_fields(self):
        fields = self._get_endpoint_list('ordering_fields')
        return fields

    def get_needs(self):
        related_models = [
            f.related_model if f.related_model else
            f.model if f.model and f.model != self.model else None
            for f in self.model._meta.get_fields()
            if f.is_relation and f.name in self.get_fields_for_serializer()
        ]
        return [{
            'app':
            model._meta.app_label,
            'singular':
            model._meta.model_name.lower(),
            'plural':
            self.inflector.pluralize(model._meta.model_name.lower()),
        } for model in related_models]

    def get_list_editable(self):
        if self.list_editable is None:
            return []
        return self.list_editable

    def get_sortable_by(self):
        return self.sortable_by

    def get_translated_fields(self):
        if self._translated_fields is None:
            models = translator.get_registered_models()
            if self.model in models:
                options = translator.get_options_for_model(self.model)
                rv = [field for field in options.fields]
                self._translated_fields = rv
            else:
                self._translated_fields = []
        return self._translated_fields

    @property
    def translated_field_names(self):
        if self._translated_field_names is None:
            rv = []
            for field in self.get_translated_fields():
                for language in self.get_languages():
                    l = language.replace('-', '_')
                    rv.append('{}_{}'.format(field, l))
            self._translated_field_names = rv
        return self._translated_field_names

    @property
    def default_language_field_names(self):
        from django.conf import settings as django_settings
        if self._default_language_field_names is None:
            l = django_settings.LANGUAGE_CODE.replace('-', '_')
            rv = []
            for field in self.get_translated_fields():
                rv.append('{}_{}'.format(field, l))
            self._default_language_field_names = rv
        return self._default_language_field_names

    def get_custom_actions(self):
        rv = []
        viewset = self.get_viewset()

        for action_name in dir(viewset):
            action = getattr(viewset, action_name)
            if getattr(action, 'action_type', None) == 'custom':
                custom_action = {
                    'url':
                    reverse('{}-{}'.format(self.get_url(),
                                           action.__name__.lower()),
                            kwargs={'pk': ':id'}),
                    'verb':
                    action.bind_to_methods[0],
                }
                custom_action.update(action.action_kwargs)
                rv.append(custom_action)

        if self.custom_actions is not None:
            rv += self.custom_actions

        return rv

    def get_bulk_actions(self):
        rv = []
        viewset = self.get_viewset()

        for action_name in dir(viewset):
            action = getattr(viewset, action_name)
            if getattr(action, 'action_type', None) == 'bulk':
                bulk_action = {
                    'url':
                    reverse('{}-{}'.format(self.get_url(),
                                           action.__name__.lower())),
                    'verb':
                    action.bind_to_methods[0],
                }
                bulk_action.update(action.action_kwargs)
                rv.append(bulk_action)

        if self.bulk_actions is not None:
            rv += []

        return rv
Exemplo n.º 6
0
def wizard(target_model,
           serializer=None,
           icon_class=None,
           btn_class=None,
           text=None,
           meta_type='custom',
           **kwargs):

    if serializer is None and target_model is not None:
        serializer = target_model
        target_model = None

    assert serializer is not None, "You need to pass a serializer to the wizard decorator"
    assert meta_type in ['custom', 'list']

    inflector_language = import_string(settings.INFLECTOR_LANGUAGE)
    inflector = Inflector(inflector_language)

    _kwargs = {
        'type': 'wizard',
        'params': {},
    }
    _kwargs.update(kwargs)
    kwargs = _kwargs

    kwargs['params']['fieldsets'] = kwargs.pop('fieldsets', None)

    serializer_instance = serializer()
    needs = []
    fields = []
    Adapter = import_string(settings.METADATA_ADAPTER)
    for field_name, field in serializer_instance.fields.items():
        if isinstance(field, PrimaryKeyRelatedField):
            model = field.queryset.model
            needs.append({
                'app':
                model._meta.app_label,
                'singular':
                model._meta.model_name.lower(),
                'plural':
                inflector.pluralize(model._meta.model_name.lower()),
            })
        fields.append(
            Adapter.adapt_field(get_field_dict(field_name, serializer)))
    kwargs['params']['needs'] = needs
    kwargs['params']['fields'] = fields
    kwargs['languages'] = get_languages()

    def decorator(func):
        def wizard_func(self, request, *args, **kwargs):
            Serializer = serializer
            serializer_instance = Serializer(data=request.data)

            if not serializer_instance.is_valid():
                return Response(serializer_instance.errors, status=400)

            request.validated_data = serializer_instance.validated_data

            return func(self, request, *args, **kwargs)

        wizard_func.__name__ = func.__name__
        if meta_type == 'custom':
            detail = True
        else:
            detail = False
        if action is not None:
            wizard_func = action(methods=[kwargs.pop('method', 'post')],
                                 detail=detail,
                                 **kwargs)  # NoQA
            wizard_func.__name__ = func.__name__
        else:
            wizard_func.bind_to_methods = [
                kwargs.pop('method', 'POST'),
            ]
            wizard_func.detail = detail
        wizard_func.action_type = meta_type
        wizard_func.wizard = True
        wizard_func.action_kwargs = action_kwargs(icon_class, btn_class, text,
                                                  wizard_func, kwargs)
        wizard_func.kwargs = {}
        if target_model is not None:
            wizard_func.action_kwargs['params']['model'] = '{}/{}/{}'.format(
                target_model._meta.app_label.lower().replace('_', '-'),
                inflector.pluralize(target_model._meta.model_name.lower()),
                wizard_func.__name__)
            print(wizard_func.action_kwargs['params']['model'])
        wizard_func.serializer = serializer

        return Adapter.adapt_wizard(wizard_func)

    return decorator
Exemplo n.º 7
0
class EnglishInflectorTestCase(unittest.TestCase):
    singular_to_plural = {
        "search": "searches",
        "switch": "switches",
        "fix": "fixes",
        "box": "boxes",
        "process": "processes",
        "address": "addresses",
        "case": "cases",
        "stack": "stacks",
        "wish": "wishes",
        "fish": "fish",
        "category": "categories",
        "query": "queries",
        "ability": "abilities",
        "agency": "agencies",
        "movie": "movies",
        "archive": "archives",
        "index": "indices",
        "wife": "wives",
        "safe": "saves",
        "half": "halves",
        "move": "moves",
        "salesperson": "salespeople",
        "person": "people",
        "spokesman": "spokesmen",
        "man": "men",
        "woman": "women",
        "basis": "bases",
        "diagnosis": "diagnoses",
        "datum": "data",
        "medium": "media",
        "analysis": "analyses",
        "node_child": "node_children",
        "child": "children",
        "experience": "experiences",
        "day": "days",
        "comment": "comments",
        "foobar": "foobars",
        "newsletter": "newsletters",
        "old_news": "old_news",
        "news": "news",
        "series": "series",
        "species": "species",
        "quiz": "quizzes",
        "perspective": "perspectives",
        "ox": "oxen",
        "photo": "photos",
        "buffalo": "buffaloes",
        "tomato": "tomatoes",
        "dwarf": "dwarves",
        "elf": "elves",
        "information": "information",
        "equipment": "equipment",
        "bus": "buses",
        "status": "statuses",
        "mouse": "mice",
        "louse": "lice",
        "house": "houses",
        "octopus": "octopi",
        "virus": "viri",
        "alias": "aliases",
        "portfolio": "portfolios",
        "vertex": "vertices",
        "matrix": "matrices",
        "axis": "axes",
        "testis": "testes",
        "crisis": "crises",
        "rice": "rice",
        "shoe": "shoes",
        "horse": "horses",
        "prize": "prizes",
        "edge": "edges"
    }

    def setUp(self):
        self.inflector = Inflector(English)

    def tearDown(self):
        self.inflector = None

    def test_pluralize(self):
        for singular in self.singular_to_plural.keys():
            assert self.inflector.pluralize(singular) == self.singular_to_plural[singular], \
            'English Inlector pluralize(%s) should produce "%s" and NOT "%s"' % (singular, self.singular_to_plural[singular], self.inflector.pluralize(singular))

    def test_singularize(self):
        for singular in self.singular_to_plural.keys():
            assert self.inflector.singularize(self.singular_to_plural[singular]) == singular, \
            'English Inlector singularize(%s) should produce "%s" and NOT "%s"' % (self.singular_to_plural[singular], singular, self.inflector.singularize(self.singular_to_plural[singular]))
Exemplo n.º 8
0
    def __new__(cls, name, bases, attrs):
        new_class = super(EndpointMetaClass,
                          cls).__new__(cls, name, bases, attrs)
        inflector = None

        processed = []

        black_list = dir(BaseEndpoint)
        model = getattr(new_class, 'model', None)

        for base in reversed(new_class.__mro__):
            for key, value in list(base.__dict__.items()):
                if key not in black_list and key not in processed and hasattr(
                        value, 'wizard') and value.wizard:
                    if getattr(value, 'action_kwargs', {}).get(
                            'params', {}).get('model', None) is None:

                        if model is not None:
                            if inflector is None:
                                inflector_language = import_string(
                                    settings.INFLECTOR_LANGUAGE)
                                inflector = Inflector(inflector_language)

                            getattr(new_class, key).action_kwargs['params'][
                                'model'] = '{}/{}/{}'.format(
                                    model._meta.app_label.lower(),
                                    inflector.pluralize(
                                        model._meta.model_name.lower()),
                                    value.__name__)

                            processed.append(key)

                    if getattr(value, 'action_kwargs', {}).get('params', {}).get('fieldsets', None) is None \
                            and getattr(new_class, 'model', None) is not None and hasattr(value, 'serializer'):

                        fieldsets_path = os.path.join(
                            django_settings.BASE_DIR,
                            new_class.__module__.rsplit('.', 1)[0],
                            'fieldsets.json')

                        try:
                            with open(fieldsets_path, 'r') as f:
                                fieldsets = json.load(f)
                                value.action_kwargs['params']['fieldsets'] = \
                                    fieldsets['{}_{}'.format(new_class.model.__name__, key)]
                        except FileNotFoundError:
                            pass
                        except KeyError:
                            pass

                        if getattr(value, 'action_kwargs', {}).get(
                                'params', {}).get('fieldsets', None) is None:
                            value.action_kwargs = getattr(
                                value, 'action_kwargs', {})
                            value.action_kwargs[
                                'params'] = value.action_kwargs.get(
                                    'params', {})
                            value.action_kwargs['params']['fieldsets'] = [{
                                'name':
                                field
                            } for field in value.serializer.Meta.fields]

        if new_class.fieldsets is None and new_class.model is not None:
            fieldsets_path = os.path.join(
                django_settings.BASE_DIR,
                new_class.__module__.rsplit('.', 1)[0], 'fieldsets.json')
            try:
                with open(fieldsets_path, 'r') as f:
                    fieldsets = json.load(f)
                    new_class.fieldsets = fieldsets[new_class.model.__name__]
            except FileNotFoundError:
                pass
            except KeyError:
                pass

        return new_class
Exemplo n.º 9
0
class SpanishInflectorTestCase(unittest.TestCase):
    singular_to_plural = {
        "álbum": "álbumes",
        "almacén": "almacenes",
        "androide": "androides",
        "antifaz": "antifaces",
        "árbol": "árboles",
        "atlas": "atlas",
        "autobús": "autobuses",
        "base": "bases",
        "bebé": "bebés",
        "camión": "camiones",
        "casa": "casas",
        "ceutí": "ceutíes",
        "chimpancé": "chimpancés",
        "clan": "clanes",
        "compás": "compases",
        "convoy": "convoyes",
        "coxis": "coxis",
        "crisis": "crisis",
        "déficit": "déficits",
        "eje": "ejes",
        "espíritu": "espíritus",
        "flash": "flashes",
        "frac": "fracs",
        "gafas": "gafas",
        "hipótesis": "hipótesis",
        "inglés": "ingleses",
        "lápiz": "lápices",
        "luz": "luces",
        "montaje": "montajes",
        "no": "noes",
        "otitis": "otitis",
        "padre": "padres",
        "país": "países",
        "papá": "papás",
        "parking": "parkings",
        "portaequipaje": "portaequipajes",
        "radiocasete": "radiocasetes",
        "show": "shows",
        "si": "sis",
        "sí": "síes",
        "tabú": "tabúes",
        "tamiz": "tamices",
        "tanque": "tanques",
        "taxi": "taxis",
        "tijeras": "tijeras",
        "tren": "trenes",
        "virus": "virus",
    }

    def setUp(self):
        self.inflector = Inflector(Spanish)

    def tearDown(self):
        self.inflector = None

    def test_pluralize(self):
        for singular, plural in self.singular_to_plural.iteritems():
            inflector_pluralize = self.inflector.pluralize(singular)
            assert inflector_pluralize == plural, \
                'Spanish Inflector pluralize(%s) should produce "%s" and NOT "%s"' % (
                    singular, plural, inflector_pluralize)

    def test_singularize(self):
        for singular, plural in self.singular_to_plural.iteritems():
            inflector_singularize = self.inflector.singularize(plural)
            assert inflector_singularize == singular, \
                'Spanish Inflector singularize(%s) should produce "%s" and NOT "%s"' % (
                    plural, singular, inflector_singularize)
Exemplo n.º 10
0
class SpanishInflectorTestCase(unittest.TestCase):
    singular_to_plural = {
        "álbum": "álbumes",
        "almacén": "almacenes",
        "androide": "androides",
        "antifaz": "antifaces",
        "árbol": "árboles",
        "atlas": "atlas",
        "autobús": "autobuses",
        "base": "bases",
        "bebé": "bebés",
        "camión": "camiones",
        "casa": "casas",
        "ceutí": "ceutíes",
        "chimpancé": "chimpancés",
        "clan": "clanes",
        "compás": "compases",
        "convoy": "convoyes",
        "coxis": "coxis",
        "crisis": "crisis",
        "déficit": "déficits",
        "eje": "ejes",
        "espíritu": "espíritus",
        "flash": "flashes",
        "frac": "fracs",
        "gafas": "gafas",
        "hipótesis": "hipótesis",    
        "inglés": "ingleses",
        "lápiz": "lápices",
        "luz": "luces",
        "montaje": "montajes",
        "no": "noes",
        "otitis": "otitis",
        "padre": "padres",
        "país": "países",
        "papá": "papás",
        "parking": "parkings",
        "portaequipaje": "portaequipajes",
        "radiocasete": "radiocasetes",
        "show": "shows",
        "si": "sis",
        "sí": "síes",
        "tabú": "tabúes",
        "tamiz": "tamices",
        "tanque": "tanques",
        "taxi": "taxis",
        "tijeras": "tijeras",
        "tren": "trenes",
        "virus": "virus",
    }

    def setUp(self):
        self.inflector = Inflector(Spanish)

    def tearDown(self):
        self.inflector = None

    def test_pluralize(self):
        for singular, plural in self.singular_to_plural.iteritems():
            inflector_pluralize = self.inflector.pluralize(singular)
            assert inflector_pluralize == plural, \
                'Spanish Inflector pluralize(%s) should produce "%s" and NOT "%s"' % (
                    singular, plural, inflector_pluralize)

    def test_singularize(self):
        for singular, plural in self.singular_to_plural.iteritems():
            inflector_singularize = self.inflector.singularize(plural)
            assert inflector_singularize == singular, \
                'Spanish Inflector singularize(%s) should produce "%s" and NOT "%s"' % (
                    plural, singular, inflector_singularize)