Projet

Général

Profil

0001-import_site-add-clean-if-empty-and-overwrite-options.patch

Benjamin Dauvergne, 03 mars 2017 21:42

Télécharger (9,46 ko)

Voir les différences:

Subject: [PATCH] import_site: add --clean, --if-empty and --overwrite options

 passerelle/apps/csvdatasource/models.py            |  6 ++--
 passerelle/base/management/commands/import_site.py | 13 ++++++-
 passerelle/base/models.py                          | 27 +++++++++-----
 passerelle/utils/__init__.py                       | 29 +++++++++++++--
 tests/test_import_export.py                        | 42 ++++++++++++++++++----
 5 files changed, 96 insertions(+), 21 deletions(-)
passerelle/apps/csvdatasource/models.py
383 383
        return d
384 384

  
385 385
    @classmethod
386
    def import_json_real(cls, d, **kwargs):
386
    def import_json_real(cls, overwrite, instance, d, **kwargs):
387 387
        queries = d.pop('queries', [])
388
        instance = super(CsvDataSource, cls).import_json_real(d, **kwargs)
388
        instance = super(CsvDataSource, cls).import_json_real(overwrite, instance, d, **kwargs)
389 389
        new = []
390
        if instance and overwrite:
391
            Query.objects.filter(resource=instance).delete()
390 392
        for query in queries:
391 393
            q = Query.import_json(query)
392 394
            q.resource = instance
passerelle/base/management/commands/import_site.py
3 3

  
4 4
from django.core.management.base import BaseCommand
5 5

  
6
from passerelle.base.models import ApiUser, BaseResource
6 7
from passerelle.utils import import_site
7 8

  
8 9

  
......
10 11
    args = '<filename>'
11 12
    help = 'Import an exported site'
12 13
    option_list = BaseCommand.option_list + (
14
        make_option('--clean', action='store_true', default=False,
15
                    help='Clean site before importing'),
13 16
        make_option('--import-users', action='store_true', default=False,
14 17
                    help='Import users and access rights'),
18
        make_option('--if-empty', action='store_true', default=False,
19
                    help='Import only if passerelle is empty'),
20
        make_option('--overwrite', action='store_true', default=False,
21
                    help='Overwirte existing resources'),
15 22
    )
16 23

  
17 24
    def handle(self, filename, **options):
18
        import_site(json.load(open(filename)), import_users=options['import_users'])
25
        import_site(json.load(open(filename)),
26
                    if_empty=options['if_empty'],
27
                    clean=options['clean'],
28
                    overwrite=options['overwrite'],
29
                    import_users=options['import_users'])
passerelle/base/models.py
62 62
        }
63 63

  
64 64
    @classmethod
65
    def import_json(self, d):
65
    def import_json(self, d, overwrite=False):
66 66
        if d.get('@type') != 'passerelle-user':
67 67
            raise ValueError('not a passerelle user export')
68 68
        d = d.copy()
69 69
        d.pop('@type')
70
        return self.objects.get_or_create(username=d['username'],
71
                                          defaults=d)
70
        api_user, created = self.objects.get_or_create(username=d['username'], defaults=d)
71
        if overwrite and not created:
72
            for key in d:
73
                setattr(api_user, key, d[key])
74
            api_user.save()
72 75

  
73 76

  
74 77
class TemplateVar(models.Model):
......
219 222
        return d
220 223

  
221 224
    @staticmethod
222
    def import_json(d, import_users=False):
225
    def import_json(d, import_users=False, overwrite=False):
223 226
        if d.get('@type') != 'passerelle-resource':
224 227
            raise ValueError('not a passerelle resource export')
225 228

  
......
228 231
        app_label, model_name = d['resource_type'].split('.')
229 232
        model = apps.get_model(app_label, model_name)
230 233
        try:
231
            return model.objects.get(slug=d['slug'])
234
            instance = model.objects.get(slug=d['slug'])
235
            if not overwrite:
236
                return
232 237
        except model.DoesNotExist:
233
            pass
238
            instance = None
234 239
        with transaction.atomic():
235 240
            # prevent semi-creation of ressources
236
            instance = model.import_json_real(d)
241
            instance = model.import_json_real(overwrite, instance, d)
237 242
            resource_type = ContentType.objects.get_for_model(instance)
238 243
            # We can only connect AccessRight objects to the new Resource after its creation
239 244
            if import_users:
......
247 252
        return instance
248 253

  
249 254
    @classmethod
250
    def import_json_real(cls, d, **kwargs):
255
    def import_json_real(cls, overwrite, instance, d, **kwargs):
251 256
        init_kwargs = {
252 257
            'title': d['title'],
253 258
            'slug': d['slug'],
......
255 260
            'log_level': d['log_level'],
256 261
        }
257 262
        init_kwargs.update(kwargs)
258
        instance = cls(**init_kwargs)
263
        if instance:
264
            for key in init_kwargs:
265
                setattr(instance, key, init_kwargs[key])
266
        else:
267
            instance = cls(**init_kwargs)
259 268
        for field, model in cls._meta.get_concrete_fields_with_model():
260 269
            if field.name == 'id':
261 270
                continue
passerelle/utils/__init__.py
192 192
    return d
193 193

  
194 194

  
195
def import_site(d, import_users=False):
195
def import_site(d, if_empty=False, clean=False, overwrite=False, import_users=False):
196 196
    '''Load passerelle configuration (users, resources and ACLs) from a dictionnary loaded from
197 197
       JSON
198 198
    '''
199 199
    d = d.copy()
200 200

  
201
    def is_empty():
202
        if import_users:
203
            if ApiUser.objects.count():
204
                return False
205

  
206
        for subclass in BaseResource.__subclasses__():
207
            if subclass._meta.abstract:
208
                continue
209
            if subclass.objects.count():
210
                return False
211
        return True
212

  
213
    if if_empty and not is_empty():
214
        return
215

  
216
    if clean:
217
        for subclass in BaseResource.__subclasses__():
218
            if subclass._meta.abstract:
219
                continue
220
            subclass.objects.all().delete()
221
        if import_users:
222
            ApiUser.objects.all().delete()
223

  
201 224
    with transaction.atomic():
202 225
        if import_users:
203 226
            for apiuser in d['apiusers']:
204
                ApiUser.import_json(apiuser)
227
                ApiUser.import_json(apiuser, overwrite=overwrite)
205 228

  
206 229
        for resource in d['resources']:
207
            BaseResource.import_json(resource, import_users=import_users)
230
            BaseResource.import_json(resource, overwrite=overwrite, import_users=import_users)
tests/test_import_export.py
87 87
    return request.param
88 88

  
89 89

  
90
def get_output_of_command(command, *args, **kwargs):
91
    old_stdout = sys.stdout
92
    output = sys.stdout = StringIO()
93
    call_command(command, *args, **kwargs)
94
    sys.stdout = old_stdout
95
    return output.getvalue()
96

  
97

  
90 98
def test_export_csvdatasource(app, setup, filetype):
91 99
    def clear():
92 100
        Query.objects.all().delete()
......
109 117
    second['resources'][0]['csv_file']['name'] = 'whocares'
110 118
    assert first == second
111 119

  
112
    old_stdout = sys.stdout
113
    output = sys.stdout = StringIO()
114
    call_command('export_site')
115
    sys.stdout = old_stdout
116

  
117
    output = output.getvalue()
120
    output = get_output_of_command('export_site')
118 121
    third = json.loads(output)
119 122
    third['resources'][0]['csv_file']['name'] = 'whocares'
120 123
    assert first == third
......
127 130
    fourth = export_site()
128 131
    fourth['resources'][0]['csv_file']['name'] = 'whocares'
129 132
    assert first == fourth
133

  
134
    Query.objects.all().delete()
135

  
136
    with tempfile.NamedTemporaryFile() as f:
137
        f.write(output)
138
        f.flush()
139
        call_command('import_site', f.name, import_users=True)
140
    assert Query.objects.count() == 0
141

  
142
    with tempfile.NamedTemporaryFile() as f:
143
        f.write(output)
144
        f.flush()
145
        call_command('import_site', f.name, overwrite=True, import_users=True)
146
    assert Query.objects.count() == 1
147

  
148
    query = Query(slug='query-2_', resource=CsvDataSource.objects.get(), structure='array')
149
    query.projections = '\n'.join(['id:int(id)', 'prenom:prenom'])
150
    query.save()
151

  
152
    assert Query.objects.count() == 2
153

  
154
    with tempfile.NamedTemporaryFile() as f:
155
        f.write(output)
156
        f.flush()
157
        call_command('import_site', f.name, clean=True, overwrite=True, import_users=True)
158
    assert Query.objects.count() == 1
159
    assert Query.objects.filter(slug='query-1_').exists()
130
-