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.
Completed
Push — master ( 3d112a...bb5fc0 )
by Bastien
42s
created

JobCrudRestController   A

Complexity

Total Complexity 4

Size/Duplication

Total Lines 31
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 31
rs 10
wmc 4

2 Methods

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