gjhiggins / Shabti (http://bel-epa.com/shabtidocs/)

A fork of tesla-pylons-elixir

Clone this repository (size: 8.5 MB): HTTPS / SSH
$ hg clone http://bitbucket.org/gjhiggins/shabti/
commit 272: c6b41202ac26
parent 271: fc1a25c06220
branch: default
Adding Vladimir Dronnikov's "entity lorem ipsum" feature to fixtures, plus small exercising test and propagation of both.
Graham Higgins
2 months ago

Changed (Δ10.8 KB):

Up to file-list shabti/templates/auth/+package+/tests/__init__.py_tmpl:

@@ -49,19 +49,16 @@ Session = elixir.session = meta.Session
49
49
50
50
class Individual(Entity):
51
51
    """Table 'Individual'.
52
53
    >>> me = Individual('Groucho')
54
55
    # 'name' field is converted to lowercase
56
    >>> me.name
57
    'groucho'
52
    
53
    used in test_models.py
54
    
55
    >>> me = Individual()
56
    
58
57
    """
59
    name = Field(String(20), unique=True)
60
    favorite_color = Field(String(20))
61
62
    def __init__(self, name, favorite_color=None):
63
        self.name = str(name).lower()
64
        self.favorite_color = favorite_color
58
    name = Field(Unicode(20), unique=True)
59
    favorite_color = Field(Unicode(20))
60
    created = Field(DateTime)
61
    active = Field(Boolean)
65
62
66
63
setup_all()
67
64

Up to file-list shabti/templates/auth_repozepylons/+package+/tests/__init__.py_tmpl:

@@ -50,19 +50,16 @@ Session = elixir.session = meta.Session
50
50
51
51
class Individual(Entity):
52
52
    """Table 'Individual'.
53
54
    >>> me = Individual('Groucho')
55
56
    # 'name' field is converted to lowercase
57
    >>> me.name
58
    'groucho'
53
    
54
    used in test_models.py
55
    
56
    >>> me = Individual()
57
    
59
58
    """
60
    name = Field(String(20), unique=True)
61
    favorite_color = Field(String(20))
62
63
    def __init__(self, name, favorite_color=None):
64
        self.name = str(name).lower()
65
        self.favorite_color = favorite_color
59
    name = Field(Unicode(20), unique=True)
60
    favorite_color = Field(Unicode(20))
61
    created = Field(DateTime)
62
    active = Field(Boolean)
66
63
67
64
setup_all()
68
65

Up to file-list shabti/templates/auth_repozewhat/+package+/tests/__init__.py_tmpl:

@@ -49,19 +49,16 @@ Session = elixir.session = meta.Session
49
49
50
50
class Individual(Entity):
51
51
    """Table 'Individual'.
52
53
    >>> me = Individual('Groucho')
54
55
    # 'name' field is converted to lowercase
56
    >>> me.name
57
    'groucho'
52
    
53
    used in test_models.py
54
    
55
    >>> me = Individual()
56
    
58
57
    """
59
    name = Field(String(20), unique=True)
60
    favorite_color = Field(String(20))
61
62
    def __init__(self, name, favorite_color=None):
63
        self.name = str(name).lower()
64
        self.favorite_color = favorite_color
58
    name = Field(Unicode(20), unique=True)
59
    favorite_color = Field(Unicode(20))
60
    created = Field(DateTime)
61
    active = Field(Boolean)
65
62
66
63
setup_all()
67
64

Up to file-list shabti/templates/auth_repozewho/+package+/tests/__init__.py_tmpl:

@@ -49,19 +49,16 @@ Session = elixir.session = meta.Session
49
49
50
50
class Individual(Entity):
51
51
    """Table 'Individual'.
52
53
    >>> me = Individual('Groucho')
54
55
    # 'name' field is converted to lowercase
56
    >>> me.name
57
    'groucho'
52
    
53
    used in test_models.py
54
    
55
    >>> me = Individual()
56
    
58
57
    """
59
    name = Field(String(20), unique=True)
60
    favorite_color = Field(String(20))
61
62
    def __init__(self, name, favorite_color=None):
63
        self.name = str(name).lower()
64
        self.favorite_color = favorite_color
58
    name = Field(Unicode(20), unique=True)
59
    favorite_color = Field(Unicode(20))
60
    created = Field(DateTime)
61
    active = Field(Boolean)
65
62
66
63
setup_all()
67
64

Up to file-list shabti/templates/authkit/+package+/tests/__init__.py_tmpl:

@@ -49,19 +49,16 @@ Session = elixir.session = meta.Session
49
49
50
50
class Individual(Entity):
51
51
    """Table 'Individual'.
52
53
    >>> me = Individual('Groucho')
54
55
    # 'name' field is converted to lowercase
56
    >>> me.name
57
    'groucho'
52
    
53
    used in test_models.py
54
    
55
    >>> me = Individual()
56
    
58
57
    """
59
    name = Field(String(20), unique=True)
60
    favorite_color = Field(String(20))
61
62
    def __init__(self, name, favorite_color=None):
63
        self.name = str(name).lower()
64
        self.favorite_color = favorite_color
58
    name = Field(Unicode(20), unique=True)
59
    favorite_color = Field(Unicode(20))
60
    created = Field(DateTime)
61
    active = Field(Boolean)
65
62
66
63
setup_all()
67
64

Up to file-list shabti/templates/authkit/+package+/tests/test_models.py:

1
from sqlalchemy.exceptions import IntegrityError
2
from {{package}}.tests import *
3
from {{package}}.tests import Session, metadata, Individual, create_all, drop_all
4
import logging
5
log = logging.getLogger(__name__)
6
7
class TestMyModel(TestModel):
8
    
9
    def test_simpleassert(self):
10
        """test description
11
        """
12
        einstein = Individual(name = u'einstein')
13
        
14
        Session.commit()
15
        
16
        ind1 = Individual.get_by(name = u'einstein')
17
        assert ind1 == einstein
18
    
19
    def test_exception(self):
20
        me = Individual(name = u'giuseppe')
21
        me_again = Individual(name = u'giuseppe')
22
        self.assertRaises(IntegrityError, Session.commit)
23
        Session.rollback()
24
    
25
    def test_many(self):
26
        from aplus.lib.fixtures import lorem_ipsum
27
        for i in xrange(5):
28
            lorem_ipsum(Individual)
29
        Session.commit()
30
        assert len(Session.query(Individual).all()) == 50
31
    

Up to file-list shabti/templates/default/+package+/lib/base.py_tmpl:

@@ -15,11 +15,12 @@ class BaseController(WSGIController):
15
15
        # WSGIController.__call__ dispatches to the Controller method
16
16
        # the request is routed to. This routing information is
17
17
        # available in environ['pylons.routes_dict']
18
18
        
19
19
        # Insert any code to be run per request here.
20
20
        
21
21
        try:
22
22
            return WSGIController.__call__(self, environ, start_response)
23
23
        finally:
24
24
            model.Session.remove()
25
25
    
26

Up to file-list shabti/templates/default/+package+/lib/fixtures.py_tmpl:

1
# -*- coding: utf-8 -*-
1
2
import types
2
3
import sys
3
4
import os
@@ -6,18 +7,26 @@ import simplejson
6
7
from {{package}} import model as model
7
8
8
9
"""
9
This module can be used for loading data into your models, for example when setting up default application data,
10
unit tests, JSON export/import and importing/exporting legacy data. Data is serialized to and from the JSON format. 
10
This module can be used for loading data into your models, for example when
11
setting up default application data, unit tests, JSON export/import and
12
importing/exporting legacy data. Data is serialized to and from the JSON
13
format.
11
14
"""
12
15
13
16
VALID_FIXTURE_FILE_EXTENSIONS = ['.json']
14
17
15
18
def load_data(model, filename=None, base_dir=None):
16
    """Installs provided fixture files into given model. Filename may be directory, file or list of dirs or files. If filename is 
17
    None, assumes that source file is located in fixtures/model_module_name/model_tablename.yaml of your application directory, 
18
    for example MyProject/fixtures/news/newsitems.yaml. The base_dir argument is the top package of the application unless 
19
    specified. You can also pass the name of a table instead of a model class."""
20
19
    """\
20
    Installs provided fixture files into given model. Filename may be a
21
    directory, file or list of dirs or files.
22
    If ``filename`` is ``None``, assumes that source file is located in
23
    :file:`fixtures/model_module_name/model_tablename.yaml` of the
24
    application directory, e.g. :file:`MyProject/fixtures/news/newsitems.yaml`.
25
    The base_dir argument is the top package of the application unless
26
    specified. You can also pass the name of a table instead of a model
27
    class.
28
    """
29
    
21
30
    if type(model) is types.StringType:
22
31
        return load_data_to_table(model, filename, base_dir)
23
32
    else:
@@ -26,17 +35,22 @@ def load_data(model, filename=None, base
26
35
        return _load_data_from_file(model, filename)
27
36
28
37
def load_data_to_table(table, filename=None, base_dir=None):
29
    """Installs data directly into a table. Useful if table does not have a corresponding model, for example a many-to-many join table.
38
    """\
39
    Installs data directly into a table. Useful if table does not have a
40
    corresponding model, for example a many-to-many join table.
30
41
    """
31
42
    
32
43
    if filename is None:
33
44
        filename = _default_fixture_path_for_table(table, base_dir)
34
45
    _load_data_to_table(table, filename)
35
    
46
36
47
def dump_data(model, filename=None, **params):
37
    """Dumps data to given destination. Params are optional arguments for selecting data. If filename is None, assumes that destination 
38
    file is located in fixtures/model_module_name/model_name_lowercase.yaml of your application directory, for example 
39
    MyProject/fixtures/news/newsitem.yaml.
48
    """\
49
    Dumps data to given destination. Params are optional arguments for
50
    selecting data.
51
    If ``filename`` is ``None``, assumes that destination file is located in
52
    :file:`fixtures/model_module_name/model_name_lowercase.yaml` of the
53
    application directory, e.g. :file:`MyProject/fixtures/news/newsitem.yaml.`
40
54
    """
41
55
    
42
56
    if filename is None:
@@ -52,7 +66,7 @@ def _default_fixture_path_for_model(mode
52
66
    module_dirs = model.__module__.split('.', 2)[-1].split('.')
53
67
    for dir in module_dirs:
54
68
        path = os.path.join(path, dir)
55
    return os.path.join(path, model.table.name + '.json')    
69
    return os.path.join(path, model.table.name + '.json')
56
70
57
71
def _default_fixture_path_for_table(table, base_dir=None):
58
72
    if base_dir is None:
@@ -66,14 +80,14 @@ def _default_fixture_path_for_table(tabl
66
80
def _is_fixture_file(filename):
67
81
    basename, ext = os.path.splitext(filename)
68
82
    return (ext.lower() in VALID_FIXTURE_FILE_EXTENSIONS)
69
    
83
70
84
def _load_data_from_dir(model, dirname):
71
85
    for dirpath, dirnames, filenames in os.walk(dirname):
72
86
        for filename in filenames:
73
87
            _load_data_from_file(model, filename)
74
    
88
75
89
def _load_data_from_file(model, filename):
76
    if not _is_fixture_file(filename): 
90
    if not _is_fixture_file(filename):
77
91
        return
78
92
    fp = file(filename, 'r')
79
93
    data = simplejson.load(fp)
@@ -86,11 +100,11 @@ def _load_data_from_file(model, filename
86
100
    elif type(data) is types.DictType:
87
101
        retval = {}
88
102
        for key, item in data.iteritems():
89
            retval[key] = _load_instance_from_dict(model, item)    
103
            retval[key] = _load_instance_from_dict(model, item)
90
104
    return retval
91
105
92
106
def _load_data_to_table(tablename, filename):
93
    if not _is_fixture_file(filename): 
107
    if not _is_fixture_file(filename):
94
108
        return
95
109
    fp = file(filename, 'r')
96
110
    data = simplejson.load(fp)
@@ -104,7 +118,7 @@ def _load_data_to_table(tablename, filen
104
118
        for key, item in data.iteritems():
105
119
            table.insert(item).execute()
106
120
    return data
107
    
121
108
122
def _dump_data_to_file(model, filename, **params):
109
123
    if params:
110
124
        queryset = model.select_by(**params)
@@ -116,9 +130,9 @@ def _dump_data_to_file(model, filename,
116
130
    fp = file(filename, 'w')
117
131
    simplejson.dump(data, fp)
118
132
    fp.close()
119
    
133
120
134
def _load_instance_from_dict(model, dict):
121
    if not dict: return 
135
    if not dict: return
122
136
    instance = model()
123
137
    fields = model._descriptor.columns.keys()
124
138
    for k, v in dict.iteritems():
@@ -135,5 +149,102 @@ def _dump_instance_to_dict(instance):
135
149
    for field in fields:
136
150
        d[field] = getattr(instance, field)
137
151
    return d
138
       
139
__all__ = ['load_data', 'dump_data']
152
153
def lorem_ipsum(entity, n=10):
154
    from random import randint, uniform, sample, choice
155
    from datetime import datetime
156
    # constants. TODO: configurable outside?
157
    _min_year = 1970
158
    _max_year = 2020
159
    _min_int = 0
160
    _max_int = 999999999
161
    _min_float = 0.0
162
    _max_float = 999999999.0
163
    _max_text = 300
164
    _max_paragraphs = 5
165
    # cache of collections of foreign key values
166
    choices = {}
167
    try:
168
        # standard generator
169
        import lipsum
170
        def _text(n):
171
            return lipsum.Generator().generate_sentence()[:n] \
172
                    if n else \
173
                    lipsum.MarkupGenerator().generate_paragraphs_plain(
174
                                                randint(1, _max_paragraphs))
175
        
176
    except:
177
        # poorman generator
178
        def _text(n):
179
            n = n or _max_text
180
            v = u''
181
            for i in xrange(randint(n/3, n)):
182
                v += choice(
183
                    u' abcdefh gijk lmn opqr stuv wx yzABCD EFG HIJ KLMNO' + \
184
                    u' PQRST UVW  XYZа бвг деё жзийк лм нопрс туфхц чшщъыь' + \
185
                    u' эюяАВБ ГДЕ ЁЖЗ ИЙКЛМНОП  РСТ У ФХЦЧ ШЩЪ ЫЬЭ ЮЯ ' + \
186
                    u'1234 567 890')
187
            return v
188
        
189
        def lorem(fields):
190
            instance = entity()
191
            for f in fields:
192
                # don't touch primary keys. 
193
                # TODO: can there be many primary keys? autoincrement instead?
194
                if f.primary_key:
195
                    continue
196
                v = 0 # reasonable default value for fields
197
                # analyse field type
198
                t = f.type.__class__.__name__
199
                # date
200
                if t in ['Date', 'DateTime']:
201
                    m = randint(1, 12)
202
                    d = 31 if m in [1, 3, 5, 7, 8, 10, 12] else 30
203
                    if m == 2: d = 28 # TODO: leaps?
204
                    v =  datetime(randint(_min_year, _max_year), m, 
205
                                  randint(1, d), randint(0, 23), 
206
                                  randint(0, 59), randint(0, 59))
207
                # boolean
208
                elif t == 'Boolean':
209
                    v = randint(0, 1)
210
                # integer
211
                elif t == 'Integer':
212
                    # foreign key -> grab possible choices from related table
213
                    if len(f.foreign_keys) > 0:
214
                        table = f.foreign_keys[0].column.table.name
215
                        v = sample(choices[table], 1)[0]
216
                    # vanilla integer
217
                    else:
218
                        v = randint(_min_int, _max_int)
219
                # float. TODO: currency subclass?
220
                elif t == 'Float':
221
                    v = uniform(_min_float, _max_float)
222
                # text. TODO: Char?
223
                elif t in ['Unicode', 'UnicodeText']:
224
                    n = f.type.length# or _max_text
225
                    v = _text(n)
226
                #import pdb; pdb.set_trace()
227
                if not v is None:
228
                    #print 'Setting [%s] to [%s]' % (f.name, v)
229
                    setattr(instance, f.name, v)
230
            instance.flush()
231
            return instance
232
        
233
    # fetch affected fields
234
    fields = entity._descriptor._columns # TODO: specify excludes? but then
235
                                         # defaults must be supplied for them!
236
    # grab possible keys for foreign keys from related tables
237
    for f in fields:
238
        if len(f.foreign_keys) > 0:
239
            table = f.foreign_keys[0].column.table
240
            #import pdb; pdb.set_trace()
241
            ids = []
242
            for r in model.session.query(table).all():
243
                ids.append(r[0])
244
            choices[table.name] = ids
245
    # generate lorem-ipsum n times
246
    for i in xrange(n):
247
        lorem(fields)
248
249
__all__ = ['load_data', 'dump_data', 'lorem_ipsum']
250

Up to file-list shabti/templates/default/+package+/tests/__init__.py_tmpl:

@@ -48,19 +48,20 @@ Session = elixir.session = meta.Session
48
48
49
49
class Individual(Entity):
50
50
    """Table 'Individual'.
51
52
    >>> me = Individual('Groucho')
53
54
    # 'name' field is converted to lowercase
55
    >>> me.name
56
    'groucho'
51
    
52
    used in test_models.py
53
    
54
    >>> me = Individual()
55
    
57
56
    """
58
    name = Field(String(20), unique=True)
59
    favorite_color = Field(String(20))
60
61
    def __init__(self, name, favorite_color=None):
62
        self.name = str(name).lower()
63
        self.favorite_color = favorite_color
57
    name = Field(Unicode(20), unique=True)
58
    favorite_color = Field(Unicode(20))
59
    created = Field(DateTime)
60
    active = Field(Boolean)
61
    
62
    # def __init__(self, name, favorite_color=None):
63
    #     self.name = str(name).lower()
64
    #     self.favorite_color = favorite_color
64
65
65
66
setup_all()
66
67
@@ -76,13 +77,15 @@ class TestModel(TestCase):
76
77
    
77
78
    def tearDown(self):
78
79
        drop_all(engine)
79
80
    
80
81
81
82
class TestController(TestModel):
82
83
    
83
84
    def __init__(self, *args, **kwargs):
84
85
        wsgiapp = pylons.test.pylonsapp
85
86
        config = wsgiapp.config
86
87
        self.app = TestApp(wsgiapp)
87
88
        url._push_object(URLGenerator(config['routes.map'], environ))
88
89
        TestCase.__init__(self, *args, **kwargs)
90
    
91

Up to file-list shabti/templates/default/+package+/tests/test_models.py_tmpl:

@@ -7,16 +7,23 @@ class TestMyModel(TestModel):
7
7
    def test_simpleassert(self):
8
8
        """test description
9
9
        """
10
        einstein = Individual('einstein')
11
10
        einstein = Individual(name = u'einstein')
11
        
12
12
        Session.commit()
13
14
        ind1 = Individual.get_by(name = 'einstein')
13
        
14
        ind1 = Individual.get_by(name = u'einstein')
15
15
        assert ind1 == einstein
16
16
        
17
17
    def test_exception(self):
18
        me = Individual('giuseppe')
19
        me_again = Individual('giuseppe')
18
        me = Individual(name = u'giuseppe')
19
        me_again = Individual(name = u'giuseppe')
20
20
        self.assertRaises(IntegrityError, Session.commit)
21
21
        Session.rollback()
22
22
    
23
    def test_many(self):
24
        from aplus.lib.fixtures import lorem_ipsum
25
        for i in xrange(5):
26
            lorem_ipsum(Individual)
27
        Session.commit()
28
        assert len(Session.query(Individual).all()) == 50
29
    

Up to file-list shabti/templates/microsite/+package+/lib/fixtures.py_tmpl:

1
# -*- coding: utf-8 -*-
1
2
import types
2
3
import sys
3
4
import os
@@ -6,18 +7,26 @@ import simplejson
6
7
from {{package}} import model as model
7
8
8
9
"""
9
This module can be used for loading data into your models, for example when setting up default application data,
10
unit tests, JSON export/import and importing/exporting legacy data. Data is serialized to and from the JSON format. 
10
This module can be used for loading data into your models, for example when
11
setting up default application data, unit tests, JSON export/import and
12
importing/exporting legacy data. Data is serialized to and from the JSON
13
format.
11
14
"""
12
15
13
16
VALID_FIXTURE_FILE_EXTENSIONS = ['.json']
14
17
15
18
def load_data(model, filename=None, base_dir=None):
16
    """Installs provided fixture files into given model. Filename may be directory, file or list of dirs or files. If filename is 
17
    None, assumes that source file is located in fixtures/model_module_name/model_tablename.yaml of your application directory, 
18
    for example MyProject/fixtures/news/newsitems.yaml. The base_dir argument is the top package of the application unless 
19
    specified. You can also pass the name of a table instead of a model class."""
20
19
    """\
20
    Installs provided fixture files into given model. Filename may be a
21
    directory, file or list of dirs or files.
22
    If ``filename`` is ``None``, assumes that source file is located in
23
    :file:`fixtures/model_module_name/model_tablename.yaml` of the
24
    application directory, e.g. :file:`MyProject/fixtures/news/newsitems.yaml`.
25
    The base_dir argument is the top package of the application unless
26
    specified. You can also pass the name of a table instead of a model
27
    class.
28
    """
29
    
21
30
    if type(model) is types.StringType:
22
31
        return load_data_to_table(model, filename, base_dir)
23
32
    else:
@@ -26,17 +35,22 @@ def load_data(model, filename=None, base
26
35
        return _load_data_from_file(model, filename)
27
36
28
37
def load_data_to_table(table, filename=None, base_dir=None):
29
    """Installs data directly into a table. Useful if table does not have a corresponding model, for example a many-to-many join table.
38
    """\
39
    Installs data directly into a table. Useful if table does not have a
40
    corresponding model, for example a many-to-many join table.
30
41
    """
31
42
    
32
43
    if filename is None:
33
44
        filename = _default_fixture_path_for_table(table, base_dir)
34
45
    _load_data_to_table(table, filename)
35
    
46
36
47
def dump_data(model, filename=None, **params):
37
    """Dumps data to given destination. Params are optional arguments for selecting data. If filename is None, assumes that destination 
38
    file is located in fixtures/model_module_name/model_name_lowercase.yaml of your application directory, for example 
39
    MyProject/fixtures/news/newsitem.yaml.
48
    """\
49
    Dumps data to given destination. Params are optional arguments for
50
    selecting data.
51
    If ``filename`` is ``None``, assumes that destination file is located in
52
    :file:`fixtures/model_module_name/model_name_lowercase.yaml` of the
53
    application directory, e.g. :file:`MyProject/fixtures/news/newsitem.yaml.`
40
54
    """
41
55
    
42
56
    if filename is None:
@@ -52,7 +66,7 @@ def _default_fixture_path_for_model(mode
52
66
    module_dirs = model.__module__.split('.', 2)[-1].split('.')
53
67
    for dir in module_dirs:
54
68
        path = os.path.join(path, dir)
55
    return os.path.join(path, model.table.name + '.json')    
69
    return os.path.join(path, model.table.name + '.json')
56
70
57
71
def _default_fixture_path_for_table(table, base_dir=None):
58
72
    if base_dir is None:
@@ -66,14 +80,14 @@ def _default_fixture_path_for_table(tabl
66
80
def _is_fixture_file(filename):
67
81
    basename, ext = os.path.splitext(filename)
68
82
    return (ext.lower() in VALID_FIXTURE_FILE_EXTENSIONS)
69
    
83
70
84
def _load_data_from_dir(model, dirname):
71
85
    for dirpath, dirnames, filenames in os.walk(dirname):
72
86
        for filename in filenames:
73
87
            _load_data_from_file(model, filename)
74
    
88
75
89
def _load_data_from_file(model, filename):
76
    if not _is_fixture_file(filename): 
90
    if not _is_fixture_file(filename):
77
91
        return
78
92
    fp = file(filename, 'r')
79
93
    data = simplejson.load(fp)
@@ -86,11 +100,11 @@ def _load_data_from_file(model, filename
86
100
    elif type(data) is types.DictType:
87
101
        retval = {}
88
102
        for key, item in data.iteritems():
89
            retval[key] = _load_instance_from_dict(model, item)    
103
            retval[key] = _load_instance_from_dict(model, item)
90
104
    return retval
91
105
92
106
def _load_data_to_table(tablename, filename):
93
    if not _is_fixture_file(filename): 
107
    if not _is_fixture_file(filename):
94
108
        return
95
109
    fp = file(filename, 'r')
96
110
    data = simplejson.load(fp)
@@ -104,7 +118,7 @@ def _load_data_to_table(tablename, filen
104
118
        for key, item in data.iteritems():
105
119
            table.insert(item).execute()
106
120
    return data
107
    
121
108
122
def _dump_data_to_file(model, filename, **params):
109
123
    if params:
110
124
        queryset = model.select_by(**params)
@@ -116,9 +130,9 @@ def _dump_data_to_file(model, filename,
116
130
    fp = file(filename, 'w')
117
131
    simplejson.dump(data, fp)
118
132
    fp.close()
119
    
133
120
134
def _load_instance_from_dict(model, dict):
121
    if not dict: return 
135
    if not dict: return
122
136
    instance = model()
123
137
    fields = model._descriptor.columns.keys()
124
138
    for k, v in dict.iteritems():
@@ -135,5 +149,102 @@ def _dump_instance_to_dict(instance):
135
149
    for field in fields:
136
150
        d[field] = getattr(instance, field)
137
151
    return d
138
       
139
__all__ = ['load_data', 'dump_data']
152
153
def lorem_ipsum(entity, n=10):
154
    from random import randint, uniform, sample, choice
155
    from datetime import datetime
156
    # constants. TODO: configurable outside?
157
    _min_year = 1970
158
    _max_year = 2020
159
    _min_int = 0
160
    _max_int = 999999999
161
    _min_float = 0.0
162
    _max_float = 999999999.0
163
    _max_text = 300
164
    _max_paragraphs = 5
165
    # cache of collections of foreign key values
166
    choices = {}
167
    try:
168
        # standard generator
169
        import lipsum
170
        def _text(n):
171
            return lipsum.Generator().generate_sentence()[:n] \
172
                    if n else \
173
                    lipsum.MarkupGenerator().generate_paragraphs_plain(
174
                                                randint(1, _max_paragraphs))
175
        
176
    except:
177
        # poorman generator
178
        def _text(n):
179
            n = n or _max_text
180
            v = u''
181
            for i in xrange(randint(n/3, n)):
182
                v += choice(
183
                    u' abcdefh gijk lmn opqr stuv wx yzABCD EFG HIJ KLMNO' + \
184
                    u' PQRST UVW  XYZа бвг деё жзийк лм нопрс туфхц чшщъыь' + \
185
                    u' эюяАВБ ГДЕ ЁЖЗ ИЙКЛМНОП  РСТ У ФХЦЧ ШЩЪ ЫЬЭ ЮЯ ' + \
186
                    u'1234 567 890')
187
            return v
188
        
189
        def lorem(fields):
190
            instance = entity()
191
            for f in fields:
192
                # don't touch primary keys. 
193
                # TODO: can there be many primary keys? autoincrement instead?
194
                if f.primary_key:
195
                    continue
196
                v = 0 # reasonable default value for fields
197
                # analyse field type
198
                t = f.type.__class__.__name__
199
                # date
200
                if t in ['Date', 'DateTime']:
201
                    m = randint(1, 12)
202
                    d = 31 if m in [1, 3, 5, 7, 8, 10, 12] else 30
203
                    if m == 2: d = 28 # TODO: leaps?
204
                    v =  datetime(randint(_min_year, _max_year), m, 
205
                                  randint(1, d), randint(0, 23), 
206
                                  randint(0, 59), randint(0, 59))
207
                # boolean
208
                elif t == 'Boolean':
209
                    v = randint(0, 1)
210
                # integer
211
                elif t == 'Integer':
212
                    # foreign key -> grab possible choices from related table
213
                    if len(f.foreign_keys) > 0:
214
                        table = f.foreign_keys[0].column.table.name
215
                        v = sample(choices[table], 1)[0]
216
                    # vanilla integer
217
                    else:
218
                        v = randint(_min_int, _max_int)
219
                # float. TODO: currency subclass?
220
                elif t == 'Float':
221
                    v = uniform(_min_float, _max_float)
222
                # text. TODO: Char?
223
                elif t in ['Unicode', 'UnicodeText']:
224
                    n = f.type.length# or _max_text
225
                    v = _text(n)
226
                #import pdb; pdb.set_trace()
227
                if not v is None:
228
                    #print 'Setting [%s] to [%s]' % (f.name, v)
229
                    setattr(instance, f.name, v)
230
            instance.flush()
231
            return instance
232
        
233
    # fetch affected fields
234
    fields = entity._descriptor._columns # TODO: specify excludes? but then
235
                                         # defaults must be supplied for them!
236
    # grab possible keys for foreign keys from related tables
237
    for f in fields:
238
        if len(f.foreign_keys) > 0:
239
            table = f.foreign_keys[0].column.table
240
            #import pdb; pdb.set_trace()
241
            ids = []
242
            for r in model.session.query(table).all():
243
                ids.append(r[0])
244
            choices[table.name] = ids
245
    # generate lorem-ipsum n times
246
    for i in xrange(n):
247
        lorem(fields)
248
249
__all__ = ['load_data', 'dump_data', 'lorem_ipsum']
250

Up to file-list shabti/templates/microsite/+package+/tests/__init__.py_tmpl:

@@ -47,6 +47,22 @@ engine = sqlalchemy.engine_from_config(c
47
47
metadata = elixir.metadata
48
48
Session = elixir.session = meta.Session
49
49
50
class Individual(Entity):
51
    """Table 'Individual'.
52
    
53
    used in test_models.py
54
    
55
    >>> me = Individual()
56
    
57
    """
58
    name = Field(Unicode(20), unique=True)
59
    favorite_color = Field(Unicode(20))
60
    created = Field(DateTime)
61
    active = Field(Boolean)
62
63
setup_all()
64
65
50
66
class TestModel(TestCase):
51
67
    
52
68
    def setUp(self):

Up to file-list shabti/templates/microsite/+package+/tests/test_models.py_tmpl:

1
from sqlalchemy.exceptions import IntegrityError
2
from {{package}}.tests import *
3
from {{package}}.tests import Session, metadata, Individual, create_all, drop_all
4
import logging
5
log = logging.getLogger(__name__)
6
7
class TestMyModel(TestModel):
8
    
9
    def test_simpleassert(self):
10
        """test description
11
        """
12
        einstein = Individual(name = u'einstein')
13
        
14
        Session.commit()
15
        
16
        ind1 = Individual.get_by(name = u'einstein')
17
        assert ind1 == einstein
18
    
19
    def test_exception(self):
20
        me = Individual(name = u'giuseppe')
21
        me_again = Individual(name = u'giuseppe')
22
        self.assertRaises(IntegrityError, Session.commit)
23
        Session.rollback()
24
    
25
    def test_many(self):
26
        from aplus.lib.fixtures import lorem_ipsum
27
        for i in xrange(5):
28
            lorem_ipsum(Individual)
29
        Session.commit()
30
        assert len(Session.query(Individual).all()) == 50
31