GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

CompanyModerationController.put()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 8
rs 9.4285
1
# -*- coding: utf-8 -*-
2
import tg
3
from sprox.widgets import TextField, TextArea
4
from tg.decorators import expose, with_trailing_slash
5
from tg.exceptions import HTTPNotFound
6
from tgext.admin import CrudRestControllerConfig
7
from tgext.admin.controller import AdminController
8
from tgext.admin.tgadminconfig import TGAdminConfig, BootstrapTGAdminConfig
9
from tgext.admin.widgets import SubmitButton
10
from tgext.admin.layouts import BootstrapAdminLayout
11
from tgext.crud import EasyCrudRestController
12
13
from pyjobsweb import model
14
from pyjobsweb.model import DBSession
15
from pyjobsweb.lib.sqlalchemy_ import kw_to_sqlalchemy, sqlalchemy_to_kw, \
16
    prepare_job_for_address_update, prepare_company_for_address_update, \
17
    prepare_company_for_validation
18
19
20 View Code Duplication
class JobGeocodingController(EasyCrudRestController):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
21
    __table_options__ = {
22
        '__limit_fields__': ['url', 'id', 'source', 'address',
23
                             'address_is_valid'],
24
        '__field_order__': ['url', 'id', 'source', 'address',
25
                            'address_is_valid'],
26
        '__xml_fields__': ['url'],
27
        'url': lambda filler, row: '<a class="btn btn-default" '
28
                                   'target="_blank" href="%(url)s">'
29
                                   '<span class="glyphicon glyphicon-link">'
30
                                   '</span>'
31
                                   '</a>' % dict(url=row.url),
32
        '__actions__': lambda filler, row:
33
            JobGeocodingController.__actions__(filler, row)
34
    }
35
36
    __form_options__ = {
37
        '__hide_fields__': ['id', 'address_is_valid'],
38
        '__omit_fields__': [
39
            'description', 'company', 'company_url', 'tags',
40
            'publication_datetime', 'title', 'publication_datetime_is_fake',
41
            'crawl_datetime', 'last_modified', 'last_sync', 'url', 'source',
42
            'geolocation_is_valid', 'latitude', 'longitude', 'pushed_on_twitter'
43
        ],
44
        '__field_widget_types__': {
45
            'address': TextField
46
        }
47
    }
48
49
    def __init__(self, session, menu_items=None):
50
        super(JobGeocodingController, self).__init__(session, menu_items)
51
52
    @classmethod
53
    def __actions__(cls, filler, row):
54
        primary_fields = filler.__provider__.get_primary_fields(filler
55
                                                                .__entity__)
56
57
        pklist = '/'.join(map(lambda x: str(getattr(row, x)), primary_fields))
58
59
        value = '''
60
            <a href="%s/edit" class="btn btn-primary">
61
                <span class="glyphicon glyphicon-pencil"></span>
62
            </a>
63
        ''' % pklist
64
65
        return value
66
67
    @expose(inherit=True)
68
    def get_all(self, *args, **kw):
69
        # Since this controller is only meant to fix invalid addresses, we only
70
        # request job offers with invalid addresses.
71
        kw['address_is_valid'] = False
72
        return super(JobGeocodingController, self).get_all(*args, **kw)
73
74
    @expose(inherit=True)
75
    def post_delete(self, *args, **kw):
76
        raise HTTPNotFound()
77
78
    @expose(inherit=True)
79
    def post(self, *args, **kw):
80
        raise HTTPNotFound()
81
82
    @expose(inherit=True)
83
    def new(self, *args, **kwargs):
84
        raise HTTPNotFound()
85
86
    @expose(inherit=True)
87
    def put(self, *args, **kw):
88
        new_model = kw_to_sqlalchemy(model.JobAlchemy, kw)
89
90
        prepare_job_for_address_update(new_model)
91
        kw = sqlalchemy_to_kw(new_model)
92
93
        return EasyCrudRestController.put(self, *args, **kw)
94
95
96 View Code Duplication
class CompanyGeocodingController(EasyCrudRestController):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
97
    __table_options__ = {
98
        '__limit_fields__': ['url', 'id', 'name', 'address',
99
                             'address_is_valid'],
100
        '__field_order__': ['url', 'id', 'name', 'address',
101
                            'address_is_valid'],
102
        '__xml_fields__': ['url'],
103
        'url': lambda filler, row: '<a class="btn btn-default" '
104
                                   'target="_blank" href="%(url)s">'
105
                                   '<span class="glyphicon glyphicon-link">'
106
                                   '</span>'
107
                                   '</a>' % dict(url=row.url),
108
        '__actions__': lambda filler, row:
109
            CompanyGeocodingController.__actions__(filler, row)
110
    }
111
112
    __form_options__ = {
113
        '__hide_fields__': ['id', 'address_is_valid'],
114
        '__omit_fields__': [
115
            'last_modified', 'last_sync', 'name', 'logo_url', 'description',
116
            'url', 'technologies', 'email', 'phone', 'latitude', 'longitude',
117
            'geolocation_is_valid', 'validated'
118
        ],
119
        '__field_widget_types__': {'address': TextField}
120
    }
121
122
    def __init__(self, session, menu_items=None):
123
        super(CompanyGeocodingController, self).__init__(session,
124
                                                         menu_items)
125
126
    @classmethod
127
    def __actions__(cls, filler, row):
128
        primary_fields = filler.__provider__.get_primary_fields(filler
129
                                                                .__entity__)
130
131
        pklist = '/'.join(map(lambda x: str(getattr(row, x)), primary_fields))
132
133
        value = '''
134
            <a href="%s/edit" class="btn btn-primary">
135
                <span class="glyphicon glyphicon-pencil"></span>
136
            </a>
137
        ''' % pklist
138
139
        return value
140
141
    @expose(inherit=True)
142
    def get_all(self, *args, **kw):
143
        # Since this controller is only meant to fix invalid addresses, we only
144
        # request job offers with invalid addresses.
145
        kw['address_is_valid'] = False
146
        return super(CompanyGeocodingController, self).get_all(*args, **kw)
147
148
    @expose(inherit=True)
149
    def post_delete(self, *args, **kw):
150
        raise HTTPNotFound()
151
152
    @expose(inherit=True)
153
    def post(self, *args, **kw):
154
        raise HTTPNotFound()
155
156
    @expose(inherit=True)
157
    def new(self, *args, **kwargs):
158
        raise HTTPNotFound()
159
160
    @expose(inherit=True)
161
    def put(self, *args, **kw):
162
        new_model = kw_to_sqlalchemy(model.CompanyAlchemy, kw)
163
164
        prepare_company_for_address_update(new_model)
165
        kw = sqlalchemy_to_kw(new_model)
166
167
        return EasyCrudRestController.put(self, *args, **kw)
168
169
170
class CompanyModerationController(EasyCrudRestController):
171
    __table_options__ = {
172
        '__limit_fields__': ['url', 'id', 'name', 'address'],
173
        '__field_order__': ['url', 'id', 'name', 'address']
174
    }
175
176
    __form_options__ = {
177
        '__hide_fields__': [
178
            'address_is_valid', 'latitude', 'longitude',
179
            'geolocation_is_valid', 'validated'
180
        ],
181
        '__omit_fields__': ['last_modified', 'last_sync'],
182
        '__field_widget_types__': {
183
            'id': TextField,
184
            'name': TextField,
185
            'logo_url': TextField,
186
            'description': TextArea,
187
            'url': TextField,
188
            'Technologies': TextArea,
189
            'address': TextField,
190
            'email': TextField,
191
            'phone': TextField
192
        }
193
    }
194
195
    def __init__(self, session, menu_items=None):
196
        super(CompanyModerationController, self).__init__(session, menu_items)
197
198
        self.edit_form.__base_widget_args__['submit'] = SubmitButton(
199
            id='submit',
200
            value='Valider',
201
            css_class='btn btn-primary'
202
        )
203
204
    @expose(inherit=True)
205
    def get_all(self, *args, **kw):
206
        # Since this controller is only meant to moderate unvalidated company
207
        # submissions, we only query for companies that aren't yet validated.
208
        kw['validated'] = False
209
        return super(CompanyModerationController, self).get_all(*args, **kw)
210
211
    @expose(inherit=True)
212
    def post(self, *args, **kw):
213
        raise HTTPNotFound()
214
215
    @expose(inherit=True)
216
    def new(self, *args, **kwargs):
217
        raise HTTPNotFound()
218
219
    @expose(inherit=True)
220
    def edit(self, *args, **kw):
221
        # This is a somewhat ugly solution. There might be a better way to add
222
        # a delete button at the end of the edit form, but I haven't found one
223
        # so far.
224
        res = EasyCrudRestController.edit(self, *args, **kw)
225
        res['delete_url'] = '/%s' % tg.request.controller_url
226
        return res
227
228
    @expose(inherit=True)
229
    def put(self, *args, **kw):
230
        new_model = kw_to_sqlalchemy(model.CompanyAlchemy, kw)
231
232
        prepare_company_for_validation(new_model)
233
        kw = sqlalchemy_to_kw(new_model)
234
235
        return EasyCrudRestController.put(self, *args, **kw)
236
237
238
class GeocodingAdminLayout(BootstrapAdminLayout):
239
    """
240
    Pyjobs' custom admin geocoding issues resolution interface layout. Redefines
241
    template_index and crud_templates from the ones used in the
242
    BootstrapAdminLayout, to match the needs of the moderation interfaces.
243
    """
244
    template_index = 'pyjobsweb.templates.admin.geocoding.index'
245
    crud_templates = {
246
        'get_all': [
247
            'mako:pyjobsweb.templates.admin.geocoding.get_all'
248
        ],
249
        'edit': [
250
            'mako:pyjobsweb.templates.admin.geocoding.edit'
251
        ]
252
        # Notice how there is no 'new' crud template? It's because the
253
        # geocoding controller isn't meant to add new rows to the database,
254
        # it's just supposed to modify existing rows. The default crud
255
        # controller of PyJobs' admin already handles new row creations.
256
    }
257
258
259
class GeocodingAdminConfig(BootstrapTGAdminConfig):
260
    """
261
    PyJobs' geocoding related issues admin controller configuration. Every
262
    tables in the model that's prone to having geocoding issues which require
263
    a custom CrustRestController to solve, should be configured in this class.
264
    See Turbogears 2's documentation for more details:
265
    http://turbogears.readthedocs.io/en/latest/turbogears/wikier/admin.html?highlight=crud
266
    """
267
    layout = GeocodingAdminLayout
268
269
    def __init__(self, models, translations=None):
270
        super(GeocodingAdminConfig, self).__init__(models, translations)
271
272
    class job(CrudRestControllerConfig):
273
        defaultCrudRestController = JobGeocodingController
274
275
    class company(CrudRestControllerConfig):
276
        defaultCrudRestController = CompanyGeocodingController
277
278
279
class ModerationAdminLayout(BootstrapAdminLayout):
280
    """
281
    Pyjobs' custom admin moderation interface layout. Redefines template_index
282
    and crud_templates from the ones used in the BootstrapAdminLayout, to match
283
    the needs of the moderation interfaces.
284
    """
285
    template_index = 'pyjobsweb.templates.admin.moderation.index'
286
    crud_templates = {
287
        'get_all': [
288
            'mako:pyjobsweb.templates.admin.moderation.get_all'
289
        ],
290
        'edit': [
291
            'mako:pyjobsweb.templates.admin.moderation.edit'
292
        ]
293
        # Notice how there is no 'new' crud template? It's because the
294
        # moderation controller isn't meant to add new rows to the database,
295
        # it's just supposed to modify existing rows. The default crud
296
        # controller of PyJobs' admin already handles new row creations.
297
    }
298
299
300
class ModerationAdminConfig(BootstrapTGAdminConfig):
301
    """
302
    PyJobs' moderation related issues admin controller configuration. Every
303
    tables in the model that's prone to moderation which require a custom
304
    CrustRestController should be configured in this class. See Turbogears 2's
305
    documentation for more details:
306
    http://turbogears.readthedocs.io/en/latest/turbogears/wikier/admin.html?highlight=crud
307
    """
308
    layout = ModerationAdminLayout
309
310
    def __init__(self, models, translations=None):
311
        super(ModerationAdminConfig, self).__init__(models, translations)
312
313
    class company(CrudRestControllerConfig):
314
        defaultCrudRestController = CompanyModerationController
315
316
317
class AdminGeocodingController(AdminController):
318 View Code Duplication
    """
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
319
    PyJobs' admin sub-controller in charge of managing geolocation issues
320
    related routing in the admin. Every table in the model that might have
321
    geocoding issues should be added to the 'to_fix' list.
322
    """
323
324
    """
325
    A list containing every tables in the Postgresql database that might have
326
    geocoding issues that should be handled by this controller.
327
    """
328
    to_fix = [model.JobAlchemy, model.CompanyAlchemy]
329
330
    def __init__(self):
331
        super(AdminGeocodingController, self).__init__(
332
            self.to_fix, DBSession, config_type=GeocodingAdminConfig)
333
334
    @property
335
    def geocoding_list(self):
336
        models = [table.__name__ for table in self.config.models.values()]
337
        models.sort()
338
339
        geocoding_list = list()
340
        for m in models:
341
            geocoding_list.append(dict(link='%ss' % m.lower(), display=m))
342
343
        return geocoding_list
344
345
    @with_trailing_slash
346
    @expose()
347
    def index(self):
348
        # We use a custom index template, therefore, we have to change the dict
349
        # that's returned by the super.index() method, so that our template gets
350
        # the values it needs to operate correctly.
351
        super_res = super(AdminGeocodingController, self).index()
352
353
        res = dict(config=super_res['config'],
354
                   model_config=super_res['model_config'],
355
                   geocoding_list=self.geocoding_list)
356
357
        return res
358
359
360
class AdminModerationController(AdminController):
361 View Code Duplication
    """
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
362
    PyJobs' admin sub-controller in charge of managing moderation related
363
    routing in the admin. Every table in the model that requires moderation
364
    should be added to the 'to_moderate' list.
365
    """
366
367
    """
368
    A list containing every tables in the Postgresql database that should be
369
    handled by this controller.
370
    """
371
    to_moderate = [model.CompanyAlchemy]
372
373
    def __init__(self):
374
        super(AdminModerationController, self).__init__(
375
            self.to_moderate, DBSession, config_type=ModerationAdminConfig)
376
377
    @property
378
    def moderation_list(self):
379
        models = [table.__name__ for table in self.config.models.values()]
380
        models.sort()
381
382
        moderation_list = list()
383
        for m in models:
384
            moderation_list.append(dict(link='%ss' % m.lower(), display=m))
385
386
        return moderation_list
387
388
    @with_trailing_slash
389
    @expose()
390
    def index(self):
391
        # We use a custom index template, therefore, we have to change the dict
392
        # that's returned by the super.index() method, so that our template gets
393
        # the values it needs to operate correctly.
394
        super_res = super(AdminModerationController, self).index()
395
396
        res = dict(config=super_res['config'],
397
                   model_config=super_res['model_config'],
398
                   moderation_list=self.moderation_list)
399
400
        return res
401
402
403
class PyJobsAdminLayout(BootstrapAdminLayout):
404
    """
405
    Pyjobs' custom admin interface layout. Redefines template_index and
406
    crud_templates from the ones used in the BootstrapAdminLayout.
407
    """
408
    template_index = 'pyjobsweb.templates.admin.default.index'
409
    crud_templates = {
410
        'get_all': [
411
            'mako:pyjobsweb.templates.admin.default.get_all'
412
        ],
413
        'edit': [
414
            'mako:pyjobsweb.templates.admin.default.edit'
415
        ],
416
        'new': [
417
            'mako:pyjobsweb.templates.admin.default.new'
418
        ]
419
    }
420
421
422
class JobCrudRestController(EasyCrudRestController):
423
    """
424
    Not a whole lot of changes in this custom EasyCrudRestController. It just
425
    adds some database synchronization code (to keep both Postgresql and
426
    Elasticsearch databases in sync.
427
    """
428
    __form_options__ = {
429
        '__omit_fields__': ['last_modified', 'last_sync']
430
    }
431
432
    def __init__(self, session, menu_items=None):
433
        super(JobCrudRestController, self).__init__(session, menu_items)
434
435
    @expose(inherit=True)
436
    def put(self, *args, **kw):
437
        # TODO: Could this test be removed if 'publication_datetime_is_fake'
438
        # TODO: was False by default and un-nullable
439
        if not kw['publication_datetime_is_fake']:
440
            kw['publication_datetime_is_fake'] = False
441
442
        old_model = model.JobAlchemy.get_job_offer(kw['id'])
443
        new_model = kw_to_sqlalchemy(model.JobAlchemy, kw)
444
445
        # Check if the address has been modified. If it's the case, then
446
        # prepare the kw dict before insertion.
447
        if old_model.address != new_model.address:
448
            prepare_job_for_address_update(new_model)
449
450
        kw = sqlalchemy_to_kw(new_model)
451
452
        return EasyCrudRestController.put(self, *args, **kw)
453
454
455
class CompanyCrudRestController(EasyCrudRestController):
456
    """
457
    Not a whole lot of changes in this custom EasyCrudRestController. It just
458
    adds some database synchronization code (to keep both Postgresql and
459
    Elasticsearch databases in sync.
460
    """
461
    __form_options__ = {
462
        '__omit_fields__': ['last_modified', 'last_sync']
463
    }
464
465
    def __init__(self, session, menu_items=None):
466
        super(CompanyCrudRestController, self).__init__(session, menu_items)
467
468
    @expose(inherit=True)
469
    def put(self, *args, **kw):
470
        old_model = model.CompanyAlchemy.get_company(kw['id'])
471
        new_model = kw_to_sqlalchemy(model.CompanyAlchemy, kw)
472
473
        # Check if the address has been modified. If it's the case, then
474
        # prepare the kw dict before insertion.
475
        if old_model.address != new_model.address:
476
            prepare_company_for_address_update(new_model)
477
478
        kw = sqlalchemy_to_kw(new_model)
479
480
        return EasyCrudRestController.put(self, *args, **kw)
481
482
483
class PyJobsAdminConfig(TGAdminConfig):
484
    """
485
    The configuration of the PyJobs admin interface. As you can see, this config
486
    uses a custom AdminLayout (which redefines some templates used by the
487
    admin - like the index for instance).
488
    We also tell Turbogears admin to use our custom CrudRestControllers when
489
    it comes to editing the jobs and the companies tables
490
    """
491
    layout = PyJobsAdminLayout
492
493
    class job(CrudRestControllerConfig):
494
        defaultCrudRestController = JobCrudRestController
495
496
    class company(CrudRestControllerConfig):
497
        defaultCrudRestController = CompanyCrudRestController
498
499
500
class PyJobsAdminController(AdminController):
501
    """
502
    This controller handles the whole admin interface of PyJobs
503
    It consists of multiple CrudRestControllers that allows one to manipulate
504
    the database.
505
    The index of this controller points to the default CrudRest AdminController,
506
    with a few modifications to ensure some model integrity issues.
507
    """
508
509
    """
510
    This sub-controller is used to solve any geocoding related issues that
511
    might have occured during the geocoding operation performed by the
512
    gearbox geocode command.
513
    """
514
    geocoding = AdminGeocodingController()
515
516
    """
517
    This controller is used to moderate (edit, delete, accept) company
518
    submissions that have been submitted, but haven't yet been moderated.
519
    """
520
    moderation = AdminModerationController()
521
522
    # The clunky 's' after the table names are enforced by the way the
523
    # framework generates the admin controllers. So, even though the plural
524
    # form in english is a tad more complicated than just appending a 's' at
525
    # the end of the words, this is how it is in TG2 and this explains why there
526
    # are these 's' at the end of the link parameters.
527
    # TODO: find a way to remove the clunky 's'
528
    geocoding_list = [
529
        {'link': 'geocoding/jobs', 'display': 'Jobs'},
530
        {'link': 'geocoding/companys', 'display': 'Companies'}
531
    ]
532
533
    moderation_list = [
534
        {'link': 'moderation/companys', 'display': 'Companies'}
535
    ]
536
537
    def __init__(self):
538
        super(PyJobsAdminController, self).__init__(
539
            model, DBSession, config_type=PyJobsAdminConfig)
540
541
    @property
542
    def model_list(self):
543
        models = [table.__name__ for table in self.config.models.values()]
544
        models.sort()
545
546
        model_list = list()
547
        for m in models:
548
            model_list.append(dict(link='%ss' % m.lower(), display=m))
549
550
        return model_list
551
552
    @with_trailing_slash
553
    @expose()
554
    def index(self):
555
        # We use a custom index template, therefore, we have to change the dict
556
        # that's returned by the super.index() method, so that our template gets
557
        # the values it needs to operate correctly.
558
        super_res = super(PyJobsAdminController, self).index()
559
560
        res = dict(config=super_res['config'],
561
                   model_config=super_res['model_config'],
562
                   model_list=self.model_list,
563
                   geocoding_list=self.geocoding_list,
564
                   moderation_list=self.moderation_list)
565
566
        return res
567