Passed
Push — master ( b60410...062e8c )
by Jordi
05:05
created

build.bika.lims.validators   F

Complexity

Total Complexity 190

Size/Duplication

Total Lines 1397
Duplicated Lines 10.81 %

Importance

Changes 0
Metric Value
wmc 190
eloc 870
dl 151
loc 1397
rs 1.73
c 0
b 0
f 0

29 Methods

Rating   Name   Duplication   Size   Complexity  
A bika.lims.validators.NIBvalidator.__call__() 0 20 2
A bika.lims.validators.UniqueFieldValidator.make_catalog_query() 0 36 4
F bika.lims.validators.CoordinateValidator.__call__() 36 80 19
A bika.lims.validators.ImportValidator.__call__() 0 15 2
A bika.lims.validators.ResultOptionsValidator.__call__() 0 25 4
A bika.lims.validators.DurationValidator.__call__() 0 15 3
A bika.lims.validators.InlineFieldValidator.__call__() 0 22 4
F bika.lims.validators.AnalysisSpecificationsValidator.validate_service() 0 35 14
A bika.lims.validators.UniqueFieldValidator.query_parent_objects() 0 27 3
A bika.lims.validators.AnalysisSpecificationsValidator.__call__() 0 27 4
A bika.lims.validators.PercentValidator.__call__() 20 20 4
B bika.lims.validators.UniqueFieldValidator.__call__() 0 38 7
B bika.lims.validators.RestrictedCategoriesValidator.__call__() 0 36 8
A bika.lims.validators.StandardIDValidator.__call__() 0 18 2
A bika.lims.validators.IdentifierValidator.__call__() 14 14 3
A bika.lims.validators.UniqueFieldValidator.get_parent_objects() 0 6 1
A bika.lims.validators.SortKeyValidator.__call__() 14 14 4
B bika.lims.validators.IBANvalidator.__call__() 0 30 5
C bika.lims.validators.ServiceKeywordValidator.__call__() 0 49 9
B bika.lims.validators.ReflexRuleValidator.__call__() 0 33 5
F bika.lims.validators.InterimFieldsValidator.__call__() 24 119 25
A bika.lims.validators.ReferenceValuesValidator.__call__() 0 17 3
A bika.lims.validators.IdentifierTypeAttributesValidator.__call__() 14 14 3
B bika.lims.validators.FormulaValidator.__call__() 0 53 7
D bika.lims.validators.UncertaintiesValidator.__call__() 0 73 12
A bika.lims.validators.NoWhiteSpaceValidator.__call__() 0 9 3
A bika.lims.validators.PrePreservationValidator.__call__() 0 24 5
C bika.lims.validators.ReferenceValuesValidator.validate_service() 0 26 9
A bika.lims.validators.InvoiceBatch_EndDate_Validator.__call__() 0 16 4

3 Functions

Rating   Name   Duplication   Size   Complexity  
A bika.lims.validators._sumLists() 0 9 3
B bika.lims.validators._toIntList() 0 15 6
A bika.lims.validators.get_record_value() 0 8 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like build.bika.lims.validators often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE.
4
#
5
# SENAITE.CORE is free software: you can redistribute it and/or modify it under
6
# the terms of the GNU General Public License as published by the Free Software
7
# Foundation, version 2.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
# details.
13
#
14
# You should have received a copy of the GNU General Public License along with
15
# this program; if not, write to the Free Software Foundation, Inc., 51
16
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
#
18
# Copyright 2018-2019 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
import re
22
import string
23
import types
24
from time import strptime as _strptime
25
26
from bika.lims import api
27
from bika.lims import bikaMessageFactory as _
28
from bika.lims import logger
29
from bika.lims.api import APIError
30
from bika.lims.utils import to_utf8
31
from Products.CMFCore.utils import getToolByName
32
from Products.CMFPlone.utils import safe_unicode
33
from Products.validation import validation
34
from Products.validation.interfaces.IValidator import IValidator
35
from Products.ZCTextIndex.ParseTree import ParseError
36
from zope.interface import implements
37
38
39 View Code Duplication
class IdentifierTypeAttributesValidator:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
40
    """Validate IdentifierTypeAttributes to ensure that attributes are
41
    not duplicated.
42
    """
43
44
    implements(IValidator)
45
    name = "identifiertypeattributesvalidator"
46
47
    def __call__(self, value, *args, **kwargs):
48
        instance = kwargs['instance']
49
        request = instance.REQUEST
50
        form = request.get('form', {})
51
        fieldname = kwargs['field'].getName()
52
        form_value = form.get(fieldname, False)
53
        if form_value is False:
54
            # not required...
55
            return True
56
        if value == instance.get(fieldname):
57
            # no change.
58
            return True
59
60
        return True
61
62
63
validation.register(IdentifierTypeAttributesValidator())
64
65
66 View Code Duplication
class IdentifierValidator:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
67
    """some actual validation should go here.
68
    I'm leaving this stub registered, but adding no extra validation.
69
    """
70
71
    implements(IValidator)
72
    name = "identifiervalidator"
73
74
    def __call__(self, value, *args, **kwargs):
75
        instance = kwargs['instance']
76
        request = instance.REQUEST
77
        form = request.get('form', {})
78
        fieldname = kwargs['field'].getName()
79
        form_value = form.get(fieldname, False)
80
        if form_value is False:
81
            # not required...
82
            return True
83
        if value == instance.get(fieldname):
84
            # no change.
85
            return True
86
87
        return True
88
89
90
validation.register(IdentifierValidator())
91
92
93
class UniqueFieldValidator:
94
    """Verifies if a field value is unique within the same container
95
    """
96
    implements(IValidator)
97
    name = "uniquefieldvalidator"
98
99
    def get_parent_objects(self, context):
100
        """Return all objects of the same type from the parent object
101
        """
102
        parent_object = api.get_parent(context)
103
        portal_type = api.get_portal_type(context)
104
        return parent_object.objectValues(portal_type)
105
106
    def query_parent_objects(self, context, query=None):
107
        """Return the objects of the same type from the parent object
108
109
        :param query: Catalog query to narrow down the objects
110
        :type query: dict
111
        :returns: Content objects of the same portal type in the parent
112
        """
113
114
        # return the object values if we have no catalog query
115
        if query is None:
116
            return self.get_parent_objects(context)
117
118
        # avoid undefined reference of catalog in except...
119
        catalog = None
120
121
        # try to fetch the results via the catalog
122
        try:
123
            catalogs = api.get_catalogs_for(context)
124
            catalog = catalogs[0]
125
            return map(api.get_object, catalog(query))
126
        except (IndexError, UnicodeDecodeError, ParseError, APIError) as e:
127
            # fall back to the object values of the parent
128
            logger.warn("UniqueFieldValidator: Catalog query {} failed "
129
                        "for catalog {} ({}) -> returning object values of {}"
130
                        .format(query, repr(catalog), str(e),
131
                                repr(api.get_parent(context))))
132
            return self.get_parent_objects(context)
133
134
    def make_catalog_query(self, context, field, value):
135
        """Create a catalog query for the field
136
        """
137
138
        # get the catalogs for the context
139
        catalogs = api.get_catalogs_for(context)
140
        # context not in any catalog?
141
        if not catalogs:
142
            logger.warn("UniqueFieldValidator: Context '{}' is not assigned"
143
                        "to any catalog!".format(repr(context)))
144
            return None
145
146
        # take the first catalog
147
        catalog = catalogs[0]
148
149
        # Check if the field accessor is indexed
150
        field_index = field.getName()
151
        accessor = field.getAccessor(context)
152
        if accessor:
153
            field_index = accessor.__name__
154
155
        # return if the field is not indexed
156
        if field_index not in catalog.indexes():
157
            return None
158
159
        # build a catalog query
160
        query = {
161
            "portal_type": api.get_portal_type(context),
162
            "path": {
163
                "query": api.get_parent_path(context),
164
                "depth": 1,
165
            }
166
        }
167
        query[field_index] = value
168
        logger.info("UniqueFieldValidator:Query={}".format(query))
169
        return query
170
171
    def __call__(self, value, *args, **kwargs):
172
        context = kwargs['instance']
173
        uid = api.get_uid(context)
174
        field = kwargs['field']
175
        fieldname = field.getName()
176
        translate = getToolByName(context, 'translation_service').translate
177
178
        # return directly if nothing changed
179
        if value == field.get(context):
180
            return True
181
182
        # Fetch the parent object candidates by catalog or by objectValues
183
        #
184
        # N.B. We want to use the catalog to speed things up, because using
185
        # `parent.objectValues` is very expensive if the parent object contains
186
        # many items and causes the UI to block too long
187
        catalog_query = self.make_catalog_query(context, field, value)
188
        parent_objects = self.query_parent_objects(
189
            context, query=catalog_query)
190
191
        for item in parent_objects:
192
            if hasattr(item, 'UID') and item.UID() != uid and \
193
               fieldname in item.Schema() and \
194
               str(item.Schema()[fieldname].get(item)) == str(value).strip():
195
                # We have to compare them as strings because
196
                # even if a number (as an  id) is saved inside
197
                # a string widget and string field, it will be
198
                # returned as an int. I don't know if it is
199
                # caused because is called with
200
                # <item.Schema()[fieldname].get(item)>,
201
                # but it happens...
202
                msg = _(
203
                    "Validation failed: '${value}' is not unique",
204
                    mapping={
205
                        'value': safe_unicode(value)
206
                    })
207
                return to_utf8(translate(msg))
208
        return True
209
210
211
validation.register(UniqueFieldValidator())
212
213
214
class InvoiceBatch_EndDate_Validator:
215
    """ Verifies that the End Date is after the Start Date """
216
217
    implements(IValidator)
218
    name = "invoicebatch_EndDate_validator"
219
220
    def __call__(self, value, *args, **kwargs):
221
        instance = kwargs.get('instance')
222
        request = kwargs.get('REQUEST')
223
224
        if request and request.form.get('BatchStartDate'):
225
            startdate = _strptime(request.form.get('BatchStartDate'), '%Y-%m-%d %H:%M')
226
        else:
227
            startdate = _strptime(instance.getBatchStartDate(), '%Y-%m-%d %H:%M')
228
229
        enddate = _strptime(value, '%Y-%m-%d %H:%M')
230
231
        translate = api.get_tool('translation_service', instance).translate
232
        if not enddate >= startdate:
233
            msg = _("Start date must be before End Date")
234
            return to_utf8(translate(msg))
235
        return True
236
237
238
validation.register(InvoiceBatch_EndDate_Validator())
239
240
241
class ServiceKeywordValidator:
242
    """Validate AnalysisService Keywords
243
    must match isUnixLikeName
244
    may not be the same as another service keyword
245
    may not be the same as any InterimField id.
246
    """
247
248
    implements(IValidator)
249
    name = "servicekeywordvalidator"
250
251
    def __call__(self, value, *args, **kwargs):
252
        instance = kwargs['instance']
253
        # fieldname = kwargs['field'].getName()
254
        # request = kwargs.get('REQUEST', {})
255
        # form = request.get('form', {})
256
257
        translate = getToolByName(instance, 'translation_service').translate
258
259
        if re.findall(r"[^A-Za-z\w\d\-\_]", value):
260
            return _("Validation failed: keyword contains invalid characters")
261
262
        # check the value against all AnalysisService keywords
263
        # this has to be done from catalog so we don't
264
        # clash with ourself
265
        bsc = getToolByName(instance, 'bika_setup_catalog')
266
        services = bsc(portal_type='AnalysisService', getKeyword=value)
267
        for service in services:
268
            if service.UID != instance.UID():
269
                msg = _(
270
                    "Validation failed: '${title}': This keyword "
271
                    "is already in use by service '${used_by}'",
272
                    mapping={
273
                        'title': safe_unicode(value),
274
                        'used_by': safe_unicode(service.Title)
275
                    })
276
                return to_utf8(translate(msg))
277
278
        calc = hasattr(instance, 'getCalculation') and \
279
            instance.getCalculation() or None
280
        our_calc_uid = calc and calc.UID() or ''
281
282
        # check the value against all Calculation Interim Field ids
283
        calcs = [c for c in bsc(portal_type='Calculation')]
284
        for calc in calcs:
285
            calc = calc.getObject()
286
            interim_fields = calc.getInterimFields()
287
            if not interim_fields:
288
                continue
289
            for field in interim_fields:
290
                if field['keyword'] == value and our_calc_uid != calc.UID():
291
                    msg = _(
292
                        "Validation failed: '${title}': This keyword "
293
                        "is already in use by calculation '${used_by}'",
294
                        mapping={
295
                            'title': safe_unicode(value),
296
                            'used_by': safe_unicode(calc.Title())
297
                        })
298
                    return to_utf8(translate(msg))
299
        return True
300
301
302
validation.register(ServiceKeywordValidator())
303
304
305
class InterimFieldsValidator:
306
    """Validating InterimField keywords.
307
        XXX Applied as a subfield validator but validates entire field.
308
        keyword must match isUnixLikeName
309
        keyword may not be the same as any service keyword.
310
        keyword must be unique in this InterimFields field
311
        keyword must be unique for interimfields which share the same title.
312
        title must be unique for interimfields which share the same keyword.
313
    """
314
315
    implements(IValidator)
316
    name = "interimfieldsvalidator"
317
318
    def __call__(self, value, *args, **kwargs):
319
        instance = kwargs['instance']
320
        fieldname = kwargs['field'].getName()
321
        request = kwargs.get('REQUEST', {})
322
        form = request.form
323
        interim_fields = form.get(fieldname, [])
324
325
        translate = getToolByName(instance, 'translation_service').translate
326
        bsc = getToolByName(instance, 'bika_setup_catalog')
327
328
        # We run through the validator once per form submit, and check all
329
        # values
330
        # this value in request prevents running once per subfield value.
331
        key = instance.id + fieldname
332
        if instance.REQUEST.get(key, False):
333
            return True
334
335
        for x in range(len(interim_fields)):
336
            row = interim_fields[x]
337
            keys = row.keys()
338
            if 'title' not in keys:
339
                instance.REQUEST[key] = to_utf8(
340
                    translate(_("Validation failed: title is required")))
341
                return instance.REQUEST[key]
342
            if 'keyword' not in keys:
343
                instance.REQUEST[key] = to_utf8(
344
                    translate(_("Validation failed: keyword is required")))
345
                return instance.REQUEST[key]
346
            if not re.match(r"^[A-Za-z\w\d\-\_]+$", row['keyword']):
347
                instance.REQUEST[key] = _(
348
                    "Validation failed: keyword contains invalid characters")
349
                return instance.REQUEST[key]
350
351
        # keywords and titles used once only in the submitted form
352
        keywords = {}
353
        titles = {}
354
        for field in interim_fields:
355
            if 'keyword' in field:
356
                if field['keyword'] in keywords:
357
                    keywords[field['keyword']] += 1
358
                else:
359
                    keywords[field['keyword']] = 1
360
            if 'title' in field:
361
                if field['title'] in titles:
362
                    titles[field['title']] += 1
363
                else:
364
                    titles[field['title']] = 1
365
        for k in [k for k in keywords.keys() if keywords[k] > 1]:
366
            msg = _(
367
                "Validation failed: '${keyword}': duplicate keyword",
368
                mapping={
369
                    'keyword': safe_unicode(k)
370
                })
371
            instance.REQUEST[key] = to_utf8(translate(msg))
372
            return instance.REQUEST[key]
373
        for t in [t for t in titles.keys() if titles[t] > 1]:
374
            msg = _(
375
                "Validation failed: '${title}': duplicate title",
376
                mapping={
377
                    'title': safe_unicode(t)
378
                })
379
            instance.REQUEST[key] = to_utf8(translate(msg))
380
            return instance.REQUEST[key]
381
382
        # check all keywords against all AnalysisService keywords for dups
383
        services = bsc(portal_type='AnalysisService', getKeyword=value)
384
        if services:
385
            msg = _(
386
                "Validation failed: '${title}': "
387
                "This keyword is already in use by service '${used_by}'",
388
                mapping={
389
                    'title': safe_unicode(value),
390
                    'used_by': safe_unicode(services[0].Title)
391
                })
392
            instance.REQUEST[key] = to_utf8(translate(msg))
393
            return instance.REQUEST[key]
394
395
        # any duplicated interimfield titles must share the same keyword
396
        # any duplicated interimfield keywords must share the same title
397
        calcs = bsc(portal_type='Calculation')
398
        keyword_titles = {}
399
        title_keywords = {}
400
        for calc in calcs:
401
            if calc.UID == instance.UID():
402
                continue
403
            calc = calc.getObject()
404
            for field in calc.getInterimFields():
405
                keyword_titles[field['keyword']] = field['title']
406
                title_keywords[field['title']] = field['keyword']
407
        for field in interim_fields:
408
            if field['keyword'] != value:
409
                continue
410 View Code Duplication
            if 'title' in field and \
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
411
               field['title'] in title_keywords.keys() and \
412
               title_keywords[field['title']] != field['keyword']:
413
                msg = _(
414
                    "Validation failed: column title '${title}' "
415
                    "must have keyword '${keyword}'",
416
                    mapping={
417
                        'title': safe_unicode(field['title']),
418
                        'keyword': safe_unicode(title_keywords[field['title']])
419
                    })
420
                instance.REQUEST[key] = to_utf8(translate(msg))
421
                return instance.REQUEST[key]
422 View Code Duplication
            if 'keyword' in field and \
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
423
               field['keyword'] in keyword_titles.keys() and \
424
               keyword_titles[field['keyword']] != field['title']:
425
                msg = _(
426
                    "Validation failed: keyword '${keyword}' "
427
                    "must have column title '${title}'",
428
                    mapping={
429
                        'keyword': safe_unicode(field['keyword']),
430
                        'title': safe_unicode(keyword_titles[field['keyword']])
431
                    })
432
                instance.REQUEST[key] = to_utf8(translate(msg))
433
                return instance.REQUEST[key]
434
435
        instance.REQUEST[key] = True
436
        return True
437
438
439
validation.register(InterimFieldsValidator())
440
441
442
class FormulaValidator:
443
    """ Validate keywords in calculation formula entry
444
    """
445
    implements(IValidator)
446
    name = "formulavalidator"
447
448
    def __call__(self, value, *args, **kwargs):
449
        if not value:
450
            return True
451
        instance = kwargs['instance']
452
        # fieldname = kwargs['field'].getName()
453
        request = kwargs.get('REQUEST', {})
454
        form = request.form
455
        interim_fields = form.get('InterimFields')
456
457
        translate = getToolByName(instance, 'translation_service').translate
458
        bsc = getToolByName(instance, 'bika_setup_catalog')
459
        interim_keywords = interim_fields and \
460
            [f['keyword'] for f in interim_fields] or []
461
        keywords = re.compile(r"\[([^\.^\]]+)\]").findall(value)
462
463
        for keyword in keywords:
464
            # Check if the service keyword exists and is active.
465
            dep_service = bsc(getKeyword=keyword, is_active=True)
466
            if not dep_service and keyword not in interim_keywords:
467
                msg = _(
468
                    "Validation failed: Keyword '${keyword}' is invalid",
469
                    mapping={
470
                        'keyword': safe_unicode(keyword)
471
                    })
472
                return to_utf8(translate(msg))
473
474
        # Wildcards
475
        # LIMS-1769 Allow to use LDL and UDL in calculations
476
        # https://jira.bikalabs.com/browse/LIMS-1769
477
        allowedwds = ['LDL', 'UDL', 'BELOWLDL', 'ABOVEUDL']
478
        keysandwildcards = re.compile(r"\[([^\]]+)\]").findall(value)
479
        keysandwildcards = [k for k in keysandwildcards if '.' in k]
480
        keysandwildcards = [k.split('.', 1) for k in keysandwildcards]
481
        errwilds = [k[1] for k in keysandwildcards if k[0] not in keywords]
482
        if len(errwilds) > 0:
483
            msg = _(
484
                "Wildcards for interims are not allowed: ${wildcards}",
485
                mapping={
486
                    'wildcards': safe_unicode(', '.join(errwilds))
487
                })
488
            return to_utf8(translate(msg))
489
490
        wildcards = [k[1] for k in keysandwildcards if k[0] in keywords]
491
        wildcards = [wd for wd in wildcards if wd not in allowedwds]
492
        if len(wildcards) > 0:
493
            msg = _(
494
                "Invalid wildcards found: ${wildcards}",
495
                mapping={
496
                    'wildcards': safe_unicode(', '.join(wildcards))
497
                })
498
            return to_utf8(translate(msg))
499
500
        return True
501
502
503
validation.register(FormulaValidator())
504
505
506
class CoordinateValidator:
507
    """ Validate latitude or longitude field values
508
    """
509
    implements(IValidator)
510
    name = "coordinatevalidator"
511
512
    def __call__(self, value, **kwargs):
513
        if not value:
514
            return True
515
516
        instance = kwargs['instance']
517
        fieldname = kwargs['field'].getName()
518
        request = instance.REQUEST
519
520
        form = request.form
521
        form_value = form.get(fieldname)
522
523
        translate = getToolByName(instance, 'translation_service').translate
524
525
        try:
526
            degrees = int(form_value['degrees'])
527
        except ValueError:
528
            return to_utf8(
529
                translate(_("Validation failed: degrees must be numeric")))
530
531
        try:
532
            minutes = int(form_value['minutes'])
533
        except ValueError:
534
            return to_utf8(
535
                translate(_("Validation failed: minutes must be numeric")))
536
537
        try:
538
            seconds = int(form_value['seconds'])
539
        except ValueError:
540
            return to_utf8(
541
                translate(_("Validation failed: seconds must be numeric")))
542
543
        if not 0 <= minutes <= 59:
544
            return to_utf8(
545
                translate(_("Validation failed: minutes must be 0 - 59")))
546
547
        if not 0 <= seconds <= 59:
548
            return to_utf8(
549
                translate(_("Validation failed: seconds must be 0 - 59")))
550
551
        bearing = form_value['bearing']
552
553 View Code Duplication
        if fieldname == 'Latitude':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
554
            if not 0 <= degrees <= 90:
555
                return to_utf8(
556
                    translate(_("Validation failed: degrees must be 0 - 90")))
557
            if degrees == 90:
558
                if minutes != 0:
559
                    return to_utf8(
560
                        translate(
561
                            _("Validation failed: degrees is 90; "
562
                              "minutes must be zero")))
563
                if seconds != 0:
564
                    return to_utf8(
565
                        translate(
566
                            _("Validation failed: degrees is 90; "
567
                              "seconds must be zero")))
568
            if bearing.lower() not in 'sn':
569
                return to_utf8(
570
                    translate(_("Validation failed: Bearing must be N/S")))
571
572 View Code Duplication
        if fieldname == 'Longitude':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
573
            if not 0 <= degrees <= 180:
574
                return to_utf8(
575
                    translate(_("Validation failed: degrees must be 0 - 180")))
576
            if degrees == 180:
577
                if minutes != 0:
578
                    return to_utf8(
579
                        translate(
580
                            _("Validation failed: degrees is 180; "
581
                              "minutes must be zero")))
582
                if seconds != 0:
583
                    return to_utf8(
584
                        translate(
585
                            _("Validation failed: degrees is 180; "
586
                              "seconds must be zero")))
587
            if bearing.lower() not in 'ew':
588
                return to_utf8(
589
                    translate(_("Validation failed: Bearing must be E/W")))
590
591
        return True
592
593
594
validation.register(CoordinateValidator())
595
596
597
class ResultOptionsValidator:
598
    """Validating AnalysisService ResultOptions field.
599
        XXX Applied as a subfield validator but validates
600
        for x in range(len(interim_fields)):
601
            row = interim_fields[x]
602
            keys = row.keys()
603
            if 'title' not in keys:entire field.
604
    """
605
606
    implements(IValidator)
607
    name = "resultoptionsvalidator"
608
609
    def __call__(self, value, *args, **kwargs):
610
        instance = kwargs['instance']
611
        fieldname = kwargs['field'].getName()
612
        request = kwargs.get('REQUEST', {})
613
        form = request.form
614
        form_value = form.get(fieldname)
615
616
        translate = getToolByName(instance, 'translation_service').translate
617
        # bsc = getToolByName(instance, 'bika_setup_catalog')
618
619
        # ResultValue must always be a number
620
        for field in form_value:
621
            try:
622
                float(field['ResultValue'])
623
            except:
624
                return to_utf8(
625
                    translate(
626
                        _("Validation failed: "
627
                          "Result Values must be numbers")))
628
            if 'ResultText' not in field:
629
                return to_utf8(
630
                    translate(
631
                        _("Validation failed: Result Text cannot be blank")))
632
633
        return True
634
635
636
validation.register(ResultOptionsValidator())
637
638
639
class RestrictedCategoriesValidator:
640
    """ Verifies that client Restricted categories include all categories
641
    required by service dependencies. """
642
643
    implements(IValidator)
644
    name = "restrictedcategoriesvalidator"
645
646
    def __call__(self, value, *args, **kwargs):
647
        instance = kwargs['instance']
648
        # fieldname = kwargs['field'].getName()
649
        # request = kwargs.get('REQUEST', {})
650
        # form = request.get('form', {})
651
652
        translate = getToolByName(instance, 'translation_service').translate
653
        bsc = getToolByName(instance, 'bika_setup_catalog')
654
        # uc = getToolByName(instance, 'uid_catalog')
655
656
        failures = []
657
658
        for category in value:
659
            if not category:
660
                continue
661
            services = bsc(
662
                portal_type="AnalysisService", getCategoryUID=category)
663
            for service in services:
664
                service = service.getObject()
665
                calc = service.getCalculation()
666
                deps = calc and calc.getDependentServices() or []
667
                for dep in deps:
668
                    if dep.getCategoryUID() not in value:
669
                        title = dep.getCategoryTitle()
670
                        if title not in failures:
671
                            failures.append(title)
672
        if failures:
673
            msg = _(
674
                "Validation failed: The selection requires the following "
675
                "categories to be selected: ${categories}",
676
                mapping={
677
                    'categories': safe_unicode(','.join(failures))
678
                })
679
            return to_utf8(translate(msg))
680
681
        return True
682
683
684
validation.register(RestrictedCategoriesValidator())
685
686
687
class PrePreservationValidator:
688
    """ Validate PrePreserved Containers.
689
        User must select a Preservation.
690
    """
691
    implements(IValidator)
692
    name = "container_prepreservation_validator"
693
694
    def __call__(self, value, *args, **kwargs):
695
        # If not prepreserved, no validation required.
696
        if not value:
697
            return True
698
699
        instance = kwargs['instance']
700
        # fieldname = kwargs['field'].getName()
701
        request = kwargs.get('REQUEST', {})
702
        form = request.form
703
        preservation = form.get('Preservation')
704
705
        if type(preservation) in (list, tuple):
706
            preservation = preservation[0]
707
708
        if preservation:
709
            return True
710
711
        translate = getToolByName(instance, 'translation_service').translate
712
        # bsc = getToolByName(instance, 'bika_setup_catalog')
713
714
        if not preservation:
715
            msg = _("Validation failed: PrePreserved containers "
716
                    "must have a preservation selected.")
717
            return to_utf8(translate(msg))
718
719
720
validation.register(PrePreservationValidator())
721
722
723
class StandardIDValidator:
724
    """Matches against regular expression:
725
       [^A-Za-z\w\d\-\_]
726
    """
727
728
    implements(IValidator)
729
    name = "standard_id_validator"
730
731
    def __call__(self, value, *args, **kwargs):
732
733
        regex = r"[^A-Za-z\w\d\-\_]"
734
735
        instance = kwargs['instance']
736
        # fieldname = kwargs['field'].getName()
737
        # request = kwargs.get('REQUEST', {})
738
        # form = request.get('form', {})
739
740
        translate = getToolByName(instance, 'translation_service').translate
741
742
        # check the value against all AnalysisService keywords
743
        if re.findall(regex, value):
744
            msg = _("Validation failed: keyword contains invalid "
745
                    "characters")
746
            return to_utf8(translate(msg))
747
748
        return True
749
750
751
validation.register(StandardIDValidator())
752
753
754
def get_record_value(request, uid, keyword, default=None):
755
    """Returns the value for the keyword and uid from the request"""
756
    value = request.get(keyword)
757
    if not value:
758
        return default
759
    if not isinstance(value, list):
760
        return default
761
    return value[0].get(uid, default) or default
762
763
764
class AnalysisSpecificationsValidator:
765
    """Min value must be below max value
766
       Warn min value must be below min value or empty
767
       Warn max value must above max value or empty
768
       Percentage value must be between 0 and 100
769
       Values must be numbers
770
    """
771
772
    implements(IValidator)
773
    name = "analysisspecs_validator"
774
775
    def __call__(self, value, *args, **kwargs):
776
        instance = kwargs['instance']
777
        request = kwargs.get('REQUEST', {})
778
        fieldname = kwargs['field'].getName()
779
780
        # This value in request prevents running once per subfield value.
781
        key = '{}{}'.format(instance.getId(), fieldname)
782
        if instance.REQUEST.get(key, False):
783
            return True
784
785
        # Walk through all AS UIDs and validate each parameter for that AS
786
        services = request.get('service', [{}])[0]
787
        for uid, service_name in services.items():
788
            err_msg = self.validate_service(request, uid)
789
            if not err_msg:
790
                continue
791
792
            # Validation failed
793
            err_msg = "{}: {}".format(_("Validation for '{}' failed"),
794
                                      _(err_msg))
795
            err_msg = err_msg.format(service_name)
796
            translate = api.get_tool('translation_service').translate
797
            instance.REQUEST[key] = to_utf8(translate(safe_unicode(err_msg)))
798
            return instance.REQUEST[key]
799
800
        instance.REQUEST[key] = True
801
        return True
802
803
    def validate_service(self, request, uid):
804
        """Validates the specs values from request for the service uid. Returns
805
        a non-translated message if the validation failed.
806
        """
807
        spec_min = get_record_value(request, uid, "min")
808
        spec_max = get_record_value(request, uid, "max")
809
        error = get_record_value(request, uid, "error", "0")
810
        warn_min = get_record_value(request, uid, "warn_min")
811
        warn_max = get_record_value(request, uid, "warn_max")
812
813
        if not spec_min and not spec_max:
814
            # Neither min nor max values have been set, dismiss
815
            return None
816
817
        if not api.is_floatable(spec_min):
818
            return "'Min' value must be numeric"
819
        if not api.is_floatable(spec_max):
820
            return "'Max' value must be numeric"
821
        if api.to_float(spec_min) > api.to_float(spec_max):
822
            return "'Max' value must be above 'Min' value"
823
        if not api.is_floatable(error) or 0.0 < api.to_float(error) > 100:
824
            return "% Error must be between 0 and 100"
825
826
        if warn_min:
827
            if not api.is_floatable(warn_min):
828
                return "'Warn Min' value must be numeric or empty"
829
            if api.to_float(warn_min) > api.to_float(spec_min):
830
                return "'Warn Min' value must be below 'Min' value"
831
832
        if warn_max:
833
            if not api.is_floatable(warn_max):
834
                return "'Warn Max' value must be numeric or empty"
835
            if api.to_float(warn_max) < api.to_float(spec_max):
836
                return "'Warn Max' value must be above 'Max' value"
837
        return None
838
839
840
validation.register(AnalysisSpecificationsValidator())
841
842
843
class UncertaintiesValidator:
844
    """Uncertainties may be specified as numeric values or percentages.
845
    Min value must be below max value.
846
    Uncertainty must not be < 0.
847
    """
848
849
    implements(IValidator)
850
    name = "uncertainties_validator"
851
852
    def __call__(self, subf_value, *args, **kwargs):
853
854
        instance = kwargs['instance']
855
        request = kwargs.get('REQUEST', {})
856
        fieldname = kwargs['field'].getName()
857
        translate = getToolByName(instance, 'translation_service').translate
858
859
        # We run through the validator once per form submit, and check all
860
        # values
861
        # this value in request prevents running once per subfield value.
862
        key = instance.id + fieldname
863
        if instance.REQUEST.get(key, False):
864
            return True
865
866
        for i, value in enumerate(request[fieldname]):
867
868
            # Values must be numbers
869
            try:
870
                minv = float(value['intercept_min'])
871
            except ValueError:
872
                instance.REQUEST[key] = to_utf8(
873
                    translate(
874
                        _("Validation failed: Min values must be numeric")))
875
                return instance.REQUEST[key]
876
            try:
877
                maxv = float(value['intercept_max'])
878
            except ValueError:
879
                instance.REQUEST[key] = to_utf8(
880
                    translate(
881
                        _("Validation failed: Max values must be numeric")))
882
                return instance.REQUEST[key]
883
884
            # values may be percentages; the rest of the numeric validation must
885
            # still pass once the '%' is stripped off.
886
            err = value['errorvalue']
887
            perc = False
888
            if err.endswith('%'):
889
                perc = True
890
                err = err[:-1]
891
            try:
892
                err = float(err)
893
            except ValueError:
894
                instance.REQUEST[key] = to_utf8(
895
                    translate(
896
                        _("Validation failed: Error values must be numeric")))
897
                return instance.REQUEST[key]
898
899
            if perc and (err < 0 or err > 100):
900
                # Error percentage must be between 0 and 100
901
                instance.REQUEST[key] = to_utf8(
902
                    translate(
903
                        _("Validation failed: Error percentage must be between 0 "
904
                          "and 100")))
905
                return instance.REQUEST[key]
906
907
            # Min value must be < max
908
            if minv > maxv:
909
                instance.REQUEST[key] = to_utf8(
910
                    translate(
911
                        _("Validation failed: Max values must be greater than Min "
912
                          "values")))
913
                return instance.REQUEST[key]
914
915
            # Error values must be >-1
916
            if err < 0:
917
                instance.REQUEST[key] = to_utf8(
918
                    translate(
919
                        _("Validation failed: Error value must be 0 or greater"
920
                          )))
921
                return instance.REQUEST[key]
922
923
        instance.REQUEST[key] = True
924
        return True
925
926
927
validation.register(UncertaintiesValidator())
928
929
930
class DurationValidator:
931
    """Simple stuff - just checking for integer values.
932
    """
933
934
    implements(IValidator)
935
    name = "duration_validator"
936
937
    def __call__(self, value, *args, **kwargs):
938
939
        instance = kwargs['instance']
940
        request = kwargs.get('REQUEST', {})
941
        fieldname = kwargs['field'].getName()
942
        translate = getToolByName(instance, 'translation_service').translate
943
944
        value = request[fieldname]
945
        for v in value.values():
946
            try:
947
                int(v)
948
            except:
949
                return to_utf8(
950
                    translate(_("Validation failed: Values must be numbers")))
951
        return True
952
953
954
validation.register(DurationValidator())
955
956
957
class ReferenceValuesValidator:
958
    """Min value must be below max value
959
       Percentage value must be between 0 and 100
960
       Values must be numbers
961
       Expected values must be between min and max values
962
    """
963
964
    implements(IValidator)
965
    name = "referencevalues_validator"
966
967
    def __call__(self, value, *args, **kwargs):
968
        request = kwargs.get('REQUEST', {})
969
        # Retrieve all AS uids
970
        services = request.get('service', [{}])[0]
971
        for uid, service_name in services.items():
972
            err_msg = self.validate_service(request, uid)
973
            if not err_msg:
974
                continue
975
976
            # Validation failed
977
            err_msg = "{}: {}".format(_("Validation for '{}' failed"),
978
                                      _(err_msg))
979
            err_msg = err_msg.format(service_name)
980
            translate = api.get_tool('translation_service').translate
981
            return to_utf8(translate(safe_unicode(err_msg)))
982
983
        return True
984
985
    def validate_service(self, request, uid):
986
        """Validates the specs values from request for the service uid. Returns
987
        a non-translated message if the validation failed."""
988
989
        result = get_record_value(request, uid, 'result')
990
        if not result:
991
            # No result set for this service, dismiss
992
            return None
993
994
        if not api.is_floatable(result):
995
            return "Expected result value must be numeric"
996
997
        spec_min = get_record_value(request, uid, "min", result)
998
        spec_max = get_record_value(request, uid, "max", result)
999
        error = get_record_value(request, uid, "error", "0")
1000
        if not api.is_floatable(spec_min):
1001
            return "'Min' value must be numeric"
1002
        if not api.is_floatable(spec_max):
1003
            return "'Max' value must be numeric"
1004
        if api.to_float(spec_min) > api.to_float(result):
1005
            return "'Min' value must be below the expected result"
1006
        if api.to_float(spec_max) < api.to_float(result):
1007
            return "'Max' value must be above the expected result"
1008
        if not api.is_floatable(error) or 0.0 < api.to_float(error) > 100:
1009
            return "% Error must be between 0 and 100"
1010
        return None
1011
1012
validation.register(ReferenceValuesValidator())
1013
1014
1015 View Code Duplication
class PercentValidator:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1016
    """ Floatable, >=0, <=100. """
1017
1018
    implements(IValidator)
1019
    name = "percentvalidator"
1020
1021
    def __call__(self, value, *args, **kwargs):
1022
        instance = kwargs['instance']
1023
        # fieldname = kwargs['field'].getName()
1024
        # request = kwargs.get('REQUEST', {})
1025
        # form = request.get('form', {})
1026
1027
        translate = getToolByName(instance, 'translation_service').translate
1028
1029
        try:
1030
            value = float(value)
1031
        except:
1032
            msg = _("Validation failed: percent values must be numbers")
1033
            return to_utf8(translate(msg))
1034
1035
        if value < 0 or value > 100:
1036
            msg = _(
1037
                "Validation failed: percent values must be between 0 and 100")
1038
            return to_utf8(translate(msg))
1039
1040
        return True
1041
1042
1043
validation.register(PercentValidator())
1044
1045
1046
def _toIntList(numstr, acceptX=0):
1047
    """
1048
    Convert ans string to a list removing all invalid characters.
1049
    Receive: a string as a number
1050
    """
1051
    res = []
1052
    # Converting and removing invalid characters
1053
    for i in numstr:
1054
        if i in string.digits and i not in string.letters:
1055
            res.append(int(i))
1056
1057
    # Converting control number into ISBN
1058
    if acceptX and (numstr[-1] in 'Xx'):
1059
        res.append(10)
1060
    return res
1061
1062
1063
def _sumLists(a, b):
1064
    """
1065
    Algorithm to check validity of NBI and NIF.
1066
    Receives string with a umber to validate.
1067
    """
1068
    val = 0
1069
    for i in map(lambda a, b: a * b, a, b):
1070
        val += i
1071
    return val
1072
1073
1074
class NIBvalidator:
1075
    """
1076
    Validates if the introduced NIB is correct.
1077
    """
1078
1079
    implements(IValidator)
1080
    name = "NIBvalidator"
1081
1082
    def __call__(self, value, *args, **kwargs):
1083
        """
1084
        Check the NIB number
1085
        value:: string with NIB.
1086
        """
1087
        instance = kwargs['instance']
1088
        translate = getToolByName(instance, 'translation_service').translate
1089
        LEN_NIB = 21
1090
        table = (73, 17, 89, 38, 62, 45, 53, 15, 50, 5, 49, 34, 81, 76, 27, 90,
1091
                 9, 30, 3)
1092
1093
        # convert to entire numbers list
1094
        nib = _toIntList(value)
1095
1096
        # checking the length of the number
1097
        if len(nib) != LEN_NIB:
1098
            msg = _('Incorrect NIB number: %s' % value)
1099
            return to_utf8(translate(msg))
1100
        # last numbers algorithm validator
1101
        return nib[-2] * 10 + nib[-1] == 98 - _sumLists(table, nib[:-2]) % 97
1102
1103
1104
validation.register(NIBvalidator())
1105
1106
1107
class IBANvalidator:
1108
    """
1109
    Validates if the introduced NIB is correct.
1110
    """
1111
1112
    implements(IValidator)
1113
    name = "IBANvalidator"
1114
1115
    def __call__(self, value, *args, **kwargs):
1116
        instance = kwargs['instance']
1117
        translate = getToolByName(instance, 'translation_service').translate
1118
1119
        # remove spaces from formatted
1120
        IBAN = ''.join(c for c in value if c.isalnum())
1121
1122
        IBAN = IBAN[4:] + IBAN[:4]
1123
        country = IBAN[-4:-2]
1124
1125
        if country not in country_dic:
1126
            msg = _('Unknown IBAN country %s' % country)
1127
            return to_utf8(translate(msg))
1128
1129
        length_c, name_c = country_dic[country]
1130
1131
        if len(IBAN) != length_c:
1132
            diff = len(IBAN) - length_c
1133
            msg = _('Wrong IBAN length by %s: %s' %
1134
                    (('short by %i' % -diff)
1135
                     if diff < 0 else ('too long by %i' % diff), value))
1136
            return to_utf8(translate(msg))
1137
        # Validating procedure
1138
        elif int("".join(str(letter_dic[x]) for x in IBAN)) % 97 != 1:
1139
            msg = _('Incorrect IBAN number: %s' % value)
1140
            return to_utf8(translate(msg))
1141
1142
        else:
1143
            # Accepted:
1144
            return True
1145
1146
1147
validation.register(IBANvalidator())
1148
1149
# Utility to check the integrity of an IBAN bank account No.
1150
# based on https://www.daniweb.com/software-development/python/code/382069
1151
# /iban-number-check-refreshed
1152
# Dictionaries - Refer to ISO 7064 mod 97-10
1153
letter_dic = {
1154
    "A": 10,
1155
    "B": 11,
1156
    "C": 12,
1157
    "D": 13,
1158
    "E": 14,
1159
    "F": 15,
1160
    "G": 16,
1161
    "H": 17,
1162
    "I": 18,
1163
    "J": 19,
1164
    "K": 20,
1165
    "L": 21,
1166
    "M": 22,
1167
    "N": 23,
1168
    "O": 24,
1169
    "P": 25,
1170
    "Q": 26,
1171
    "R": 27,
1172
    "S": 28,
1173
    "T": 29,
1174
    "U": 30,
1175
    "V": 31,
1176
    "W": 32,
1177
    "X": 33,
1178
    "Y": 34,
1179
    "Z": 35,
1180
    "0": 0,
1181
    "1": 1,
1182
    "2": 2,
1183
    "3": 3,
1184
    "4": 4,
1185
    "5": 5,
1186
    "6": 6,
1187
    "7": 7,
1188
    "8": 8,
1189
    "9": 9
1190
}
1191
1192
# ISO 3166-1 alpha-2 country code
1193
country_dic = {
1194
    "AL": [28, "Albania"],
1195
    "AD": [24, "Andorra"],
1196
    "AT": [20, "Austria"],
1197
    "BE": [16, "Belgium"],
1198
    "BA": [20, "Bosnia"],
1199
    "BG": [22, "Bulgaria"],
1200
    "HR": [21, "Croatia"],
1201
    "CY": [28, "Cyprus"],
1202
    "CZ": [24, "Czech Republic"],
1203
    "DK": [18, "Denmark"],
1204
    "EE": [20, "Estonia"],
1205
    "FO": [18, "Faroe Islands"],
1206
    "FI": [18, "Finland"],
1207
    "FR": [27, "France"],
1208
    "DE": [22, "Germany"],
1209
    "GI": [23, "Gibraltar"],
1210
    "GR": [27, "Greece"],
1211
    "GL": [18, "Greenland"],
1212
    "HU": [28, "Hungary"],
1213
    "IS": [26, "Iceland"],
1214
    "IE": [22, "Ireland"],
1215
    "IL": [23, "Israel"],
1216
    "IT": [27, "Italy"],
1217
    "LV": [21, "Latvia"],
1218
    "LI": [21, "Liechtenstein"],
1219
    "LT": [20, "Lithuania"],
1220
    "LU": [20, "Luxembourg"],
1221
    "MK": [19, "Macedonia"],
1222
    "MT": [31, "Malta"],
1223
    "MU": [30, "Mauritius"],
1224
    "MC": [27, "Monaco"],
1225
    "ME": [22, "Montenegro"],
1226
    "NL": [18, "Netherlands"],
1227
    "NO": [15, "Northern Ireland"],
1228
    "PO": [28, "Poland"],
1229
    "PT": [25, "Portugal"],
1230
    "RO": [24, "Romania"],
1231
    "SM": [27, "San Marino"],
1232
    "SA": [24, "Saudi Arabia"],
1233
    "RS": [22, "Serbia"],
1234
    "SK": [24, "Slovakia"],
1235
    "SI": [19, "Slovenia"],
1236
    "ES": [24, "Spain"],
1237
    "SE": [24, "Sweden"],
1238
    "CH": [21, "Switzerland"],
1239
    "TR": [26, "Turkey"],
1240
    "TN": [24, "Tunisia"],
1241
    "GB": [22, "United Kingdom"]
1242
}
1243
1244
1245 View Code Duplication
class SortKeyValidator:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1246
    """ Check for out of range values.
1247
    """
1248
1249
    implements(IValidator)
1250
    name = "SortKeyValidator"
1251
1252
    def __call__(self, value, *args, **kwargs):
1253
        instance = kwargs['instance']
1254
        translate = getToolByName(instance, 'translation_service').translate
1255
        try:
1256
            value = float(value)
1257
        except:
1258
            msg = _("Validation failed: value must be float")
1259
            return to_utf8(translate(msg))
1260
1261
        if value < 0 or value > 1000:
1262
            msg = _("Validation failed: value must be between 0 and 1000")
1263
            return to_utf8(translate(msg))
1264
1265
        return True
1266
1267
1268
validation.register(SortKeyValidator())
1269
1270
1271
class InlineFieldValidator:
1272
    """ Inline Field Validator
1273
1274
    calls a field function for validation
1275
    """
1276
1277
    implements(IValidator)
1278
    name = "inline_field_validator"
1279
1280
    def __call__(self, value, *args, **kwargs):
1281
        field = kwargs['field']
1282
        request = kwargs['REQUEST']
1283
        instance = kwargs['instance']
1284
1285
        # extract the request values
1286
        data = request.get(field.getName())
1287
1288
        # check if the field contains a callable
1289
        validator = getattr(field, self.name, None)
1290
1291
        # validator is a callable
1292
        if callable(validator):
1293
            return validator(instance, request, field, data)
1294
1295
        # validator is a string, check if the instance has a method with this name
1296
        if type(validator) in types.StringTypes:
1297
            instance_validator = getattr(instance, validator, None)
1298
            if callable(instance_validator):
1299
                return instance_validator(request, field, data)
1300
1301
        return True
1302
1303
1304
validation.register(InlineFieldValidator())
1305
1306
1307
class ReflexRuleValidator:
1308
    """
1309
    - The analysis service have to be related to the method
1310
    """
1311
1312
    implements(IValidator)
1313
    name = "reflexrulevalidator"
1314
1315
    def __call__(self, value, *args, **kwargs):
1316
1317
        instance = kwargs['instance']
1318
        # fieldname = kwargs['field'].getName()
1319
        # request = kwargs.get('REQUEST', {})
1320
        # form = request.get('form', {})
1321
        method = instance.getMethod()
1322
        bsc = getToolByName(instance, 'bika_setup_catalog')
1323
        query = {
1324
            'portal_type': 'AnalysisService',
1325
            'getAvailableMethodUIDs': method.UID()
1326
        }
1327
        method_ans_uids = [b.UID for b in bsc(query)]
1328
        rules = instance.getReflexRules()
1329
        error = ''
1330
        pc = getToolByName(instance, 'portal_catalog')
1331
        for rule in rules:
1332
            as_uid = rule.get('analysisservice', '')
1333
            as_brain = pc(
1334
                UID=as_uid,
1335
                portal_type='AnalysisService',
1336
                is_active=True)
1337
            if as_brain[0] and as_brain[0].UID in method_ans_uids:
1338
                pass
1339
            else:
1340
                error += as_brain['title'] + ' '
1341
        if error:
1342
            translate = getToolByName(instance,
1343
                                      'translation_service').translate
1344
            msg = _("The following analysis services don't belong to the"
1345
                    "current method: " + error)
1346
            return to_utf8(translate(msg))
1347
        return True
1348
1349
1350
validation.register(ReflexRuleValidator())
1351
1352
1353
class NoWhiteSpaceValidator:
1354
    """ String, not containing space(s). """
1355
1356
    implements(IValidator)
1357
    name = "no_white_space_validator"
1358
1359
    def __call__(self, value, *args, **kwargs):
1360
        instance = kwargs['instance']
1361
        translate = getToolByName(instance, 'translation_service').translate
1362
1363
        if value and " " in value:
1364
            msg = _("Invalid value: Please enter a value without spaces.")
1365
            return to_utf8(translate(msg))
1366
1367
        return True
1368
1369
1370
validation.register(NoWhiteSpaceValidator())
1371
1372
1373
class ImportValidator(object):
1374
    """Checks if a dotted name can be imported or not
1375
    """
1376
    implements(IValidator)
1377
    name = "importvalidator"
1378
1379
    def __call__(self, mod, **kwargs):
1380
1381
        # some needed tools
1382
        instance = kwargs['instance']
1383
        translate = getToolByName(instance, 'translation_service').translate
1384
1385
        try:
1386
            # noinspection PyUnresolvedReferences
1387
            import importlib
1388
            importlib.import_module(mod)
1389
        except ImportError:
1390
            msg = _("Validation failed: Could not import module '%s'" % mod)
1391
            return to_utf8(translate(msg))
1392
1393
        return True
1394
1395
1396
validation.register(ImportValidator())
1397