mirror of
https://github.com/Tiendil/pynames.git
synced 2025-12-26 14:58:48 +01:00
initial implementaton with some tests
This commit is contained in:
parent
4cb61830cf
commit
94483a13d2
7 changed files with 250 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
*.pyc
|
||||
*~
|
||||
25
LICENSE
Normal file
25
LICENSE
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
Copyright 2012 Aleksey Yeletsky. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are
|
||||
permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
of conditions and the following disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those of the
|
||||
authors and should not be interpreted as representing official policies, either expressed
|
||||
or implied, of <copyright holder>.
|
||||
1
pynames/__init__.py
Normal file
1
pynames/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
# coding: utf-8
|
||||
32
pynames/fixtures/test_from_list_generator.json
Normal file
32
pynames/fixtures/test_from_list_generator.json
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"names": [ {"native_language": "ru",
|
||||
"genders": {"m": {"ru": "ru_m_name_1",
|
||||
"en": "en_m_name_1"}}},
|
||||
|
||||
{"native_language": "ru",
|
||||
"genders": {"f": {"ru": "ru_f_name_2",
|
||||
"en": "en_f_name_2"}}},
|
||||
|
||||
{"native_language": "ru",
|
||||
"genders": {"f": {"ru": "ru_f_name_3",
|
||||
"en": "en_f_name_3"}}},
|
||||
|
||||
{"native_language": "ru",
|
||||
"genders": {"m": {"ru": "ru_m_name_4",
|
||||
"en": "en_m_name_4"},
|
||||
"f": {"ru": "ru_f_name_4",
|
||||
"en": "en_f_name_4"}}},
|
||||
|
||||
{"native_language": "ru",
|
||||
"genders": {"m": {"ru": "ru_m_name_5",
|
||||
"en": "en_m_name_5"},
|
||||
"f": {"ru": "ru_f_name_5",
|
||||
"en": "en_f_name_5"}}},
|
||||
|
||||
{"native_language": "ru",
|
||||
"genders": {"m": {"ru": "ru_m_name_5",
|
||||
"en": "en_m_name_5"},
|
||||
"f": {"ru": "ru_f_name_5",
|
||||
"en": "en_f_name_5"}}}
|
||||
]
|
||||
}
|
||||
98
pynames/generators.py
Normal file
98
pynames/generators.py
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
# coding: utf-8
|
||||
|
||||
import json
|
||||
import random
|
||||
|
||||
|
||||
class PynamesException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class GENDER:
|
||||
MALE = 'm'
|
||||
FEMALE = 'f'
|
||||
MF = ['m', 'f']
|
||||
|
||||
ALL = ['m', 'f']
|
||||
|
||||
|
||||
class LANGUAGE:
|
||||
RU = 'ru'
|
||||
EN = 'en'
|
||||
NATIVE = 'native'
|
||||
|
||||
ALL = ['ru', 'en', 'native']
|
||||
|
||||
|
||||
class BaseGenerator(object):
|
||||
pass
|
||||
|
||||
|
||||
class Name(object):
|
||||
|
||||
__slots__ = ('genders', 'native_language', 'translations')
|
||||
|
||||
def __init__(self, data):
|
||||
self.native_language = data['native_language']
|
||||
self.genders = frozenset(data['genders'].keys())
|
||||
self.translations = data['genders']
|
||||
|
||||
def get_for(self, gender, language=LANGUAGE.NATIVE):
|
||||
if language == LANGUAGE.NATIVE:
|
||||
language = self.native_language
|
||||
return self.translations[gender][language]
|
||||
|
||||
def exists_for(self, genders):
|
||||
return genders & self.genders
|
||||
|
||||
def __unicode__(self):
|
||||
for gender in GENDER.ALL:
|
||||
if gender in self.genders:
|
||||
return self.translations[gender][self.native_language]
|
||||
error_msg = 'Name: can not get default value for name with data: %r' % self.genders
|
||||
raise PynamesException(error_msg)
|
||||
|
||||
def __str__(self): return self.__unicode__()
|
||||
|
||||
|
||||
class FromListGenerator(BaseGenerator):
|
||||
|
||||
SOURCE = None
|
||||
|
||||
def __init__(self):
|
||||
self.names_list = []
|
||||
self.choices = {}
|
||||
|
||||
if self.SOURCE is None:
|
||||
error_msg = 'FromListGenerator: you must make subclass of FromListGenerator and defined attribute SOURCE in it.'
|
||||
raise NotImplementedError(error_msg)
|
||||
|
||||
with open(self.SOURCE) as f:
|
||||
names_data = json.load(f)
|
||||
for name_data in names_data['names']:
|
||||
self.names_list.append(Name(name_data))
|
||||
|
||||
if not self.names_list:
|
||||
raise PynamesException('FromListGenerator: no names loaded from "%s"' % self.SOURCE)
|
||||
|
||||
def _get_cache_key(self, genders):
|
||||
return '_'.join(genders)
|
||||
|
||||
def _get_slice(self, genders):
|
||||
key = self._get_cache_key(genders)
|
||||
genders = frozenset(genders)
|
||||
if key not in self.choices:
|
||||
self.choices[key] = [name_record
|
||||
for name_record in self.names_list
|
||||
if name_record.exists_for(genders)]
|
||||
return self.choices[key]
|
||||
|
||||
def get_names_number(self, genders=GENDER.ALL):
|
||||
return len(self._get_slice(genders))
|
||||
|
||||
def get_name(self, genders=GENDER.ALL):
|
||||
return random.choice(self._get_slice(genders))
|
||||
|
||||
def get_name_simple(self, gender=GENDER.MALE, language=LANGUAGE.NATIVE):
|
||||
name = self.get_name(genders=[gender])
|
||||
return name.get_for(gender, language)
|
||||
5
pynames/russian/__init__.py
Normal file
5
pynames/russian/__init__.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# coding: utf-8
|
||||
|
||||
import os
|
||||
|
||||
|
||||
87
pynames/tests.py
Normal file
87
pynames/tests.py
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# coding: utf-8
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from .generators import Name, GENDER, LANGUAGE, FromListGenerator, PynamesException
|
||||
|
||||
FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixtures')
|
||||
|
||||
class TestName(unittest.TestCase):
|
||||
|
||||
def test_base(self):
|
||||
name = Name({'native_language': 'ru',
|
||||
'genders': {'m': {'ru': 'ru_name'}}})
|
||||
self.assertEqual(unicode(name), 'ru_name')
|
||||
self.assertEqual(name.get_for(GENDER.MALE, LANGUAGE.RU), 'ru_name')
|
||||
self.assertEqual(name.get_for(GENDER.MALE), 'ru_name')
|
||||
|
||||
def test_genders(self):
|
||||
name = Name({'native_language': 'ru',
|
||||
'genders': {'m': {'ru': 'ru_m_name'},
|
||||
'f': {'ru': 'ru_f_name'}}})
|
||||
self.assertEqual(unicode(name), 'ru_m_name')
|
||||
self.assertEqual(name.get_for(GENDER.MALE, LANGUAGE.RU), 'ru_m_name')
|
||||
self.assertEqual(name.get_for(GENDER.FEMALE, LANGUAGE.RU), 'ru_f_name')
|
||||
|
||||
def test_languages(self):
|
||||
name = Name({'native_language': 'ru',
|
||||
'genders': {'m': {'ru': 'ru_m_name',
|
||||
'en': 'en_m_name'},
|
||||
'f': {'ru': 'ru_f_name',
|
||||
'en': 'en_f_name'}}})
|
||||
self.assertEqual(unicode(name), 'ru_m_name')
|
||||
self.assertEqual(name.get_for(GENDER.MALE, LANGUAGE.RU), 'ru_m_name')
|
||||
self.assertEqual(name.get_for(GENDER.FEMALE, LANGUAGE.RU), 'ru_f_name')
|
||||
self.assertEqual(name.get_for(GENDER.MALE, LANGUAGE.EN), 'en_m_name')
|
||||
self.assertEqual(name.get_for(GENDER.FEMALE, LANGUAGE.EN), 'en_f_name')
|
||||
self.assertEqual(name.get_for(GENDER.MALE), 'ru_m_name')
|
||||
self.assertEqual(name.get_for(GENDER.FEMALE), 'ru_f_name')
|
||||
|
||||
|
||||
class TestFromListGenerator(unittest.TestCase):
|
||||
|
||||
class TestGenerator(FromListGenerator):
|
||||
SOURCE = os.path.join(FIXTURES_DIR, 'test_from_list_generator.json')
|
||||
|
||||
NAMES_RU_MALE = ['ru_m_name_1', 'ru_m_name_4', 'ru_m_name_5', 'ru_m_name_6']
|
||||
NAMES_EN_MALE = ['en_m_name_1', 'en_m_name_4', 'en_m_name_5', 'en_m_name_6']
|
||||
NAMES_RU_FEMALE = ['ru_f_name_2', 'ru_f_name_3', 'ru_f_name_4', 'ru_f_name_5', 'ru_f_name_6']
|
||||
NAMES_EN_FEMALE = ['en_f_name_2', 'en_f_name_3', 'en_f_name_4', 'en_f_name_5', 'en_f_name_6']
|
||||
|
||||
def test_not_derived(self):
|
||||
self.assertRaises(NotImplementedError, FromListGenerator)
|
||||
|
||||
def test_wrong_path(self):
|
||||
class WrongGenerator(FromListGenerator):
|
||||
SOURCE = ''
|
||||
self.assertRaises(IOError, WrongGenerator)
|
||||
|
||||
def test_base(self):
|
||||
generator = self.TestGenerator()
|
||||
self.assertEqual(generator.get_names_number(), 6)
|
||||
self.assertTrue(generator.get_name_simple() in self.NAMES_RU_MALE)
|
||||
|
||||
def test_male_female_selection(self):
|
||||
generator = self.TestGenerator()
|
||||
self.assertEqual(generator.get_names_number(genders=[GENDER.MALE]), 4)
|
||||
self.assertEqual(generator.get_names_number(genders=[GENDER.FEMALE]), 5)
|
||||
|
||||
def test_get_name(self):
|
||||
generator = self.TestGenerator()
|
||||
|
||||
for i in xrange(100):
|
||||
name = generator.get_name_simple(gender=GENDER.MALE, language=LANGUAGE.RU)
|
||||
self.assertTrue(name in self.NAMES_RU_MALE)
|
||||
|
||||
for i in xrange(100):
|
||||
name = generator.get_name_simple(gender=GENDER.FEMALE, language=LANGUAGE.RU)
|
||||
self.assertTrue(name in self.NAMES_RU_FEMALE)
|
||||
|
||||
for i in xrange(100):
|
||||
name = generator.get_name_simple(gender=GENDER.MALE, language=LANGUAGE.EN)
|
||||
self.assertTrue(name in self.NAMES_EN_MALE)
|
||||
|
||||
for i in xrange(100):
|
||||
name = generator.get_name_simple(gender=GENDER.FEMALE, language=LANGUAGE.EN)
|
||||
self.assertTrue(name in self.NAMES_EN_FEMALE)
|
||||
Loading…
Add table
Add a link
Reference in a new issue