def languages_save(): (form, region, region_languages) = validate_region() # languages were chosen for the selected region selected_languages = map(urlunquote, request.forms.getall('language')) has_invalid = any(lang not in region_languages for lang in selected_languages) if not selected_languages or has_invalid: # some of the chosen languages were invalid or no languages were chosen return dict(form=form, region=region, region_languages=region_languages, selected_languages=selected_languages, # Translators, message displayed as help text for # language selection message=_("Please select languages from the list below.")) # languages are valid, store them in setup file and inform other about it exts.setup.append({'contentfilter.region': region, 'contentfilter.languages': selected_languages}) set_fsal_whitelist(request.app.config) return dict(form=form, region=region, region_languages=region_languages, selected_languages=selected_languages, message=_("Content filter has been set."), redirect_url=i18n_url('dashboard:main'))
def send_confirmation(email=None, next_path=None): if email is None: form = ConfirmationForm(request.params) if not form.is_valid(): return template('confirmation', form=form) email = form.processed_data['email'] next_path = next_path or request.params.get('next', '/') if request.user.is_authenticated: redirect_url = next_path redirect_target = _("your previous location") else: login_path = request.app.get_url('login') redirect_url = get_redirect_path(login_path, next_path) redirect_target = _('log-in') expiration = request.app.config['authentication.confirmation_expires'] confirmation_key = create_temporary_key(email, expiration) task_runner = request.app.config['task.runner'] task_runner.schedule(send_mail, email, _("Confirm registration"), text='email/confirm', data={'confirmation_key': confirmation_key, 'next_path': next_path}, config=request.app.config) return template('feedback', page_title=_('Account registration complete'), status='email', redirect_url=redirect_url, message=_('Confirmation email has been sent to ' '{address}. Check your inbox.').format( address=email), redirect_target=redirect_target)
def update_content_details(id): to_put = [] content = get_content_or_404(id) ref_path = i18n_path(request.forms.get('back', content.path)) if not content.is_editable: response.flash(_('Voting is disabled for content that is being ' 'broadcast')) redirect(ref_path) vote = request.forms.get('vote') if vote not in ['up', 'down']: response.flash(_('There was a problem with the request. Please try ' 'again later.')) redirect(ref_path) if vote == 'up': content.upvotes += 1 to_put.append(Event.create(Event.UPVOTE, content.key)) elif vote == 'down': content.downvotes += 1 to_put.append(Event.create(Event.DOWNVOTE, content.key)) to_put.append(content) ndb.put_multi(to_put) redirect(ref_path)
def update_content_details(id): content = get_content_or_404(id) if not content.is_editable: # Translators, shown when content is not editable (it's on air, etc) response.flash(_('This content is not editable')) redirect(i18n_path(content.path)) errors = {} title = request.forms.getunicode('title', '').strip() license = request.forms.get('license') or None if not content.title and not title: errors['title'] = _('Title cannot be blank') if license and license not in Content.LICENSE_CHOICES: errors['license'] = _('Please select a license from provided choices') if not errors: to_put = [] if title and content.title != title: content.title = title to_put.append(Event.create(Event.TITLE, content.key)) if license and content.license != license: content.license = license to_put.append(Event.create(Event.LICENSE, content.key)) if to_put: # If we have events in to_put list, we also need to put the content to_put.append(content) ndb.put_multi(to_put) response.flash(_('Content has been updated')) redirect(content.path) return dict(vals=request.forms, errors=erorrs, content=content)
def manage_downloads(): """ Manage the downloaded content """ action_handlers = {'add': add, 'add_all': add_all, 'delete': delete, 'delete_all': delete_all} action = request.forms.get('action') file_list = request.forms.getall('selection') try: handler = action_handlers[action] except KeyError: # Translators, used as error title shown to user when wrong action # code is submitted to server title = _("Invalid action") # Translators, used as error message shown to user when wrong action # code is submitted to server message = _('Invalid action, please use one of the form buttons.') status = 'error' feedback = dict(page_title=title, message=message, redirect_url=i18n_url('downloads:list'), redirect_target=_("Updates")) else: status = 'success' feedback = handler(file_list) return dict(status=status, **feedback)
def reset(): next_path = request.params.get('next', '/') form = PasswordResetForm(request.params) if request.user.is_authenticated: # Set arbitrary non-empty value to prevent form error. We don't really # care about this field otherwise. form.reset_token.bind_value('not needed') if not form.is_valid(): return dict(next_path=next_path, form=form) if request.user.is_authenticated: username = request.user.username else: user = get_user_by_reset_token(form.processed_data['reset_token']) if not user: form._error = ValidationError('invalid_token', {'value': ''}) return dict(next_path=next_path, form=form) username = user.username set_password(username, form.processed_data['password1']) if request.user.is_authenticated: request.user.logout() login_url = i18n_url('auth:login_form') + set_qparam( next=next_path).to_qs() return template('feedback.tpl', # Translators, used as page title on feedback page page_title=_('New password was set'), # Translators, used as link label on feedback page in "You # will be taken to log-in page..." redirect_target=_('log-in page'), # Translators, shown after password has been changed message=_("Password for username '%(username)s' has been " "set.") % {'username': username}, status='success', redirect_url=login_url)
def do_enter(): """Enter numbers into database""" numbers = set(parse_numbers(request.forms.get('numbers', ''))) timestamp = datetime.datetime.now() usr_hash = get_fingerprint(request) result_num = [] # TODO make place variable, depending on current request q = Place.select().where(Place.place == 'LAGESO') lageso = q.get() if q.count() == 1 else None if not numbers: result_num.append(_('novalidnumbers')) else: for num in numbers: if is_valid_number(num): try: n = Number.create(number=num.upper(), time=timestamp, place=lageso, fingerprint=usr_hash) result_num.append(n.number) except IntegrityError: try: n = Number.get(Number.number == num.upper()) # FIXME Why ain't there any value placeholder in translation string? result_num.append(_(u'erruniquenumber') + ': {}'.format(n.number)) except DoesNotExist: result_num.append(u'Something weired happend with {}'.format(num)) # FIXME result_num is horrible, as it contains success and failures, indistinguishable return {'entered': result_num, 'timestamp': timestamp.strftime('%x %X')}
def send_payment_confirmation(item, stripe_obj, email, config): interval_types = { 'month': _("monthly"), 'year': _("annual") } item_types = { 'twitter': _("twitter feed"), 'content': _("content"), } is_subscription = stripe_obj.object == 'customer' if is_subscription: card = stripe_obj.sources.data[-1] last4digits = card.last4 subscription = stripe_obj.subscriptions.data[-1] interval = interval_types[subscription.plan.interval] timestamp = subscription.start amount = subscription.plan.amount else: last4digits = stripe_obj.source.last4 interval = None timestamp = stripe_obj.created amount = stripe_obj.amount context_data = {'email': email, 'item_type': item_types[item.type], 'last4digits': last4digits, 'timestamp': datetime.datetime.fromtimestamp(timestamp), 'total_amount': humanize_amount(amount, config=config), 'interval': interval, 'is_subscription': is_subscription} send_mail(email, _("Payment Confirmation"), text='email/payment_confirmation', data=context_data, config=config)
def perform_backup(): dbpath = get_dbpath() bpath = get_backup_path() try: btime = backup(dbpath, bpath) logging.debug('Database backup took %s seconds', btime) except AssertionError as err: # Translators, error message displayed if database backup fails base_msg = _('Database backup could not be completed. ' 'The following error occurred:') message = ' '.join(map(unicode, [base_msg, err.message])) status = 'error' url = i18n_url('dashboard:main') # Translators, redirection target if database backup was successful target = _('Dashboard') else: # Translators, message displayed if database backup was successful base_msg = _('Database backup has been completed successfully.') took_msg = lazy_ngettext('The operation took %s second', 'The operation took %s seconds', btime) % round(btime, 2) message = ' '.join(map(unicode, [base_msg, took_msg])) status = 'success' url = get_file_url() # Translators, redirection target if database backup was successful target = _('the backup folder') # Translators, used as page title title = _('Database backup') return dict(status=status, page_title=title, message=message, redirect_url=url, redirect_target=target)
def content_sites_list(): """ Show list of multipage content only """ result = prepare_content_list(multipage=True) result.update({'base_path': i18n_url('content:sites_list'), 'page_title': _('Sites'), 'empty_message': _('There are no sites in the library.')}) return result
def content_list(): """ Show list of content """ result = prepare_content_list() result.update({'base_path': i18n_url('content:list'), 'page_title': _('Library'), 'empty_message': _('Content library is currently empty')}) return result
def reset(): reset_token = request.params.get('reset_token') form = EmergencyResetForm(request.params) if not form.is_valid(): return dict(form=form, reset_token=reset_token) db = request.db.sessions query = db.Delete('users') db.query(query) query = db.Delete('sessions') db.query(query) username = form.processed_data['username'] create_user(username, form.processed_data['password1'], is_superuser=True, db=request.db.sessions, overwrite=True, reset_token=reset_token) return template('feedback.tpl', # Translators, used as page title on feedback page page_title=_('Emergency reset successful'), # Translators, used as link label on feedback page in "You # will be taken to log-in page..." redirect_target=_('log-in page'), # Translators, shown after emergency reset message=_("You may now log in as " "'%(username)s'.") % {'username': username}, status='success', redirect_url=i18n_url('auth:login_form'))
def password_reset_request(): next_path = request.params.get('next', '/') form = PasswordResetRequestForm(request.params) if not form.is_valid(): return template('password_reset_request', form=form) email = form.processed_data['email'] if get_user(email): expires = request.app.config['authentication.password_reset_expires'] reset_key = create_temporary_key(email, expires) task_runner = request.app.config['task.runner'] task_runner.schedule(send_mail, email, _("Reset Password"), text='email/password_reset', data={'reset_key': reset_key, 'next_path': next_path}, config=request.app.config) redirect_url = get_redirect_path(request.app.get_url('login'), next_path) return template('feedback', page_title=_('Password reset email sent'), status='email', redirect_url=redirect_url, message=_('An email with a password reset link has been ' 'sent to {address}. Check your inbox.').format( address=email), redirect_target=_('log-in'))
def form_valid(self): if self.form.processed_data['action'] == self.form.UPLOAD_OPERATION: # Translators, message displayed when overlay upload was successful return dict(message=_("Overlay successfully uploaded.")) # Translators, message displayed when overlay operation was successful return dict(message=_("Overlay operation successfully completed. " "Please restart the device for the changes " "to take effect."))
def validate(self, value): try: return dateutil.parser.parse(value) except ValueError as exc: msg = self.message or _("Invalid date: {0}") raise ValidationError(msg.format(exc), {'value': value}) except TypeError as exc: msg = self.message or _("Invalid input.") raise ValidationError(msg, {'value': value})
def confirm(key): next_path = request.params.get('next', '/') redir_onfail = get_redirect_path(request.app.get_url('login'), next_path) try: email = confirm_user(key) except KeyExpired: return {'message': _("The confirmation key has already expired."), 'page_title': _("Confirmation"), 'status': 'error', 'redirect_url': redir_onfail, 'redirect_target': _('log-in')} except KeyNotFound: return {'message': _("The confirmation key is not valid."), 'page_title': _("Confirmation"), 'status': 'error', 'redirect_url': redir_onfail, 'redirect_target': _('log-in')} else: login_user_no_auth(email) return {'message': _("E-mail address successfully confirmed. You have " "been automatically logged in."), 'page_title': _("Confirmation"), 'status': 'success', 'redirect_url': next_path, 'redirect_target': _('the main page')}
def show_file_list(path='.'): search = request.params.get('p') resp_format = request.params.get('f', '') conf = request.app.config is_missing = False is_search = False files = init_filemanager() if search: relpath = '.' up = '' dirs, file_list = files.get_search_results(search) is_search = True if not len(file_list) and len(dirs) == 1: redirect(i18n_url('files:path', path=dirs[0].path.replace('\\', '/'))) if not dirs and not file_list: is_missing = True readme = _('The files you were looking for could not be found') else: readme = _('This list represents the search results') else: is_search = False try: dir_contents = files.get_dir_contents(path) (path, relpath, dirs, file_list, readme) = dir_contents except files.DoesNotExist: is_missing = True relpath = '.' dirs = [] file_list = [] readme = _('This folder does not exist') except files.IsFileError as err: if resp_format == 'json': fstat = os.stat(path) response.content_type = 'application/json' return json.dumps(dict( name=os.path.basename(path), size=fstat[stat.ST_SIZE], )) options = {'download': request.params.get('filename', False)} return static_file(err.path, root=files.filedir, **options) up = os.path.normpath(os.path.join(path, '..')) up = os.path.relpath(up, conf['content.filedir']) if resp_format == 'json': response.content_type = 'application/json' return json.dumps(dict( dirs=dirs, files=dictify_file_list(file_list), readme=to_unicode(readme), is_missing=is_missing, is_search=is_search, )) return dict(path=relpath, dirs=dirs, files=file_list, up=up, readme=readme, is_missing=is_missing, is_search=is_search)
def wrapper(**kwargs): item_type = kwargs.pop('item_type', None) item_id = kwargs.pop('item_id', None) if not item_type or not item_id: abort(404, _("The specified item was not found.")) item = get_item(item_type, id=item_id) if not item: abort(404, _("The specified item was not found.")) return func(item=item, **kwargs)
def removal_failed(self, path): # Translators, used as page title of unsuccessful file removal feedback page_title = _("File not removed") # Translators, used as message of unsuccessful file removal feedback message = _("File could not be removed.") body = self.template_func('ui/feedback.tpl', status='error', page_title=page_title, message=message, redirect_url=get_parent_url(path), redirect_target=_("file list")) return self.HTTPResponse(body)
def show_broadcast_priority_scheduled(item): if item.charge_id is None: # attempted access to success-page, while not charged priority_url = request.app.get_url('broadcast_priority_form', item_type=item.type, item_id=item.id) redirect(priority_url) return dict(item=item, status='success', page_title=_('Thank You'), message=_('Your payment has been completed. You will receive' ' an email with your receipt shortly.'))
def remove_content(content): """ Delete a single piece of content from archive """ archive = open_archive() archive.remove_from_archive([content.md5]) request.app.exts.cache.invalidate(prefix='content') # Translators, used as page title of successful content removal feedback page_title = _("Content removed") # Translators, used as message of successful content removal feedback message = _("Content successfully removed.") return dict(status='success', page_title=page_title, message=message, redirect_url=i18n_url('content:list'), redirect_target=_("Library"))
def already_removed(self, path): # Translators, used as page title when a file's removal is # retried, but it was already deleted before page_title = _("File already removed") # Translators, used as message when a file's removal is # retried, but it was already deleted before message = _("The specified file has already been removed.") body = self.template_func('ui/feedback.tpl', status='success', page_title=page_title, message=message, redirect_url=get_parent_url(path), redirect_target=_("Files")) return self.HTTPResponse(body)
def validate(self, value): try: if self.min_value is not None and self.min_value > value: msg = self.message or _("Input value is too small.") msg = msg.format(min=self.min_value, max=self.max_value) raise ValidationError(msg, {'value': value}) if self.max_value is not None and self.max_value < value: msg = self.message or _("Input value is too large.") msg = msg.format(min=self.min_value, max=self.max_value) raise ValidationError(msg, {'value': value}) except Exception: msg = self.message or _("Invalid input.") msg = msg.format(min=self.min_value, max=self.max_value) raise ValidationError(msg, {'value': value})
def delete(file_list): spooldir = request.app.config['content.spooldir'] removed_count = downloads.remove_downloads(spooldir, content_ids=file_list) # Translators, used as confirmation title after the chosen updates were # deleted on the updates page title = _("Updates deleted") # Translators, used as confirmation message after the chosen updates were # deleted on the updates page message = lazy_ngettext("An update has been deleted.", "{update_count} updates have been deleted.", removed_count).format(update_count=removed_count) return dict(page_title=title, message=message, redirect_url=i18n_url('downloads:list'), redirect_target=_("Updates"))
def ago(dt, days_only=False): # It may appear as if there's quite a bit of redundancy here, but it all # boils down to the need to mark translations using ngettext. We can't be # too 'progammatic' about this because various languages have different # numeber of plural forms and different rules about plural calculations. # Because of this, we sacrifice DRY for tranlsator-friendly strings. diff = utcnow().date() - dt divdays = functools.partial(divround, diff.days) period = divdays(365) if period: return ngettext("{number} year ago", "{number} years ago", period).format(number=period) period = divdays(30) if period: return ngettext("{number} month ago", "{number} months ago", period).format(number=period) period = divdays(7) if period: return ngettext("{number} week ago", "{number} weeks ago", period).format(number=period) if diff.days > 1: return ngettext("{number} day ago", "{number} days ago", diff.days).format(number=diff.days) if diff.days == 1: return _('Yesterday') if days_only: return _('Today') divsecs = functools.partial(divround, diff.seconds) period = divsecs(3600) if period: return ngettext("{number} hour ago", "{number} hours ago", period).format(number=period) period = divsecs(60) if period: return ngettext("{number} minute ago", "{number} minutes ago", period).format(number=period) if diff.seconds > 5: return ngettext("{number} second ago", "{number} seconds ago", diff.seconds).format(number=diff.seconds) return _('just now')
def prepare_content_list(multipage=None): # parse search query query = request.params.getunicode('q', '').strip() # parse language filter default_lang = request.user.options.get('content_language', None) lang = request.params.get('lang', default_lang) request.user.options['content_language'] = lang # parse tag filter archive = open_archive() try: tag = int(request.params.get('tag')) except (TypeError, ValueError): tag = None tag_name = None else: try: tag_name = archive.get_tag_name(tag)['name'] except (IndexError, KeyError): abort(404, _('Specified tag was not found')) # parse pagination params page = Paginator.parse_page(request.params) per_page = Paginator.parse_per_page(request.params) # get content list filtered by above parsed filter params metas = filter_content(query, lang, tag, multipage) pager = Paginator(metas, page, per_page) return dict(metadata=pager.items, pager=pager, vals=request.params.decode(), query=query, lang=dict(lang=lang), tag=tag_name, tag_id=tag, tag_cloud=archive.get_tag_cloud())
def parse(self, value): chosen = unicode(value) for (candidate, label) in self.choices: if unicode(candidate) == chosen: return chosen if value is not None else value raise ValueError(_("This is not a valid choice."))
def validate_datetime(dt_str): try: parse_datetime(dt_str) except ValueError as exc: return str(exc) except TypeError: return _("Invalid input.")
def wrapper(path, **kwargs): manager = Manager(request.app.supervisor) if not manager.exists(path): # Translators, used as page title when a file's removal is # retried, but it was already deleted before title = _("File already removed") # Translators, used as message when a file's removal is # retried, but it was already deleted before message = _("The specified file has already been removed.") return template('feedback', status='success', page_title=title, message=message, redirect_url=get_parent_url(path), redirect_target=_("Files")) return func(path=path, **kwargs)
def password_reset(key): next_path = request.params.get('next', '/') form = PasswordResetForm(request.forms) if not form.is_valid(): return template('password_reset', form=form, next_path=next_path) key = form.processed_data['key'] new_password = form.processed_data['new_password1'] reset_password(key, new_password) redirect_url = get_redirect_path(request.app.get_url('login'), next_path) return template('feedback', page_title=_('Password reset successful'), status='success', redirect_url=redirect_url, message=_('You have successfully reset your password.'), redirect_target=_('log-in'))
def form_valid(self): exts.cache.set(FIRMWARE_UPDATE_KEY, 'processing') firmware = self.form.processed_data['firmware'] try: path = exts.config['firmware.save_path'] exts.tasks.schedule(update_firmware, args=(firmware, path)) except Exception: logging.exception('Firmware upload error.') # Translators, shown when firmware upload failed return dict(saved=False, message=_('Firmware upload failed.')) else: return dict(saved=True)
def save_settings(): settings = request.app.supervisor.exts.settings form_cls = settings.get_form() form = form_cls(request.forms) if not form.is_valid(): return dict(form=form, groups=settings.groups) request.app.supervisor.exts.setup.append(form.processed_data) request.app.supervisor.exts.events.publish('SETTINGS_SAVED', form.processed_data) return dict(form=form, groups=settings.groups, message=_('Settings saved.'), redirect_url=i18n_url('dashboard:main'))
class LengthValidator(Validator): """ Validates that value's length is within specified range. This validator works with any objects that support the ``len()`` function. The ``min_len`` and ``max_len`` arguments are used to set the lower and upper bounds respectively. The check for those bounds are only done when the arguments are supplied so, when both arguments are omitted, this validator is effectively pass-through. :error name(s): min_len, max_len """ messages = { 'min_len': _('Must be at least {len} long'), 'max_len': _('Must not be longer than {len}'), } def __init__(self, min_len=None, max_len=None, **kwargs): self.min_len = min_len self.max_len = max_len super(LengthValidator, self).__init__(**kwargs) def validate(self, value): if not value: return if self.min_len is not None and len(value) < self.min_len: raise ValidationError('min_len', { 'value': value, 'len': self.min_len }) if self.max_len is not None and len(value) > self.max_len: raise ValidationError('max_len', { 'value': value, 'len': self.max_len })
class InRangeValidator(Validator): """ Validates that value is within a range between two values. This validator works with any objects that support ``>`` and ``<`` operators. The ``min_value`` and ``max_value`` arguments are used to set the lower and upper bounds respectively. The check for those bounds are only done when the arguments are supplied so, when both arguments are omitted, this validator is effectively pass-through. :error name(s): min_val, max_val """ messages = { 'min_val': _('{value} is too small'), 'max_val': _('{value} is too large'), } def __init__(self, min_value=None, max_value=None, **kwargs): self.min_value = min_value self.max_value = max_value super(InRangeValidator, self).__init__(**kwargs) def validate(self, value): if value is None: return if self.min_value is not None and self.min_value > value: raise ValidationError('min_val', { 'value': value, 'min': self.min_value }) if self.max_value is not None and self.max_value < value: raise ValidationError('max_val', { 'value': value, 'max': self.max_value })
def do_enter(): """Enter numbers into database""" numbers = set(parse_numbers(request.forms.get('numbers', ''))) timestamp = datetime.datetime.now() usr_hash = get_fingerprint(request) result_num = [] # TODO make place variable, depending on current request q = Place.select().where(Place.place == 'LAGESO') lageso = q.get() if q.count() == 1 else None if not numbers: result_num.append(_('novalidnumbers')) else: for num in numbers: if is_valid_number(num): try: n = Number.create(number=num.upper(), time=timestamp, place=lageso, fingerprint=usr_hash) result_num.append(n.number) except IntegrityError: try: n = Number.get(Number.number == num.upper()) # FIXME Why ain't there any value placeholder in translation string? result_num.append( _(u'erruniquenumber') + ': {}'.format(n.number)) except DoesNotExist: result_num.append( u'Something weired happend with {}'.format(num)) # FIXME result_num is horrible, as it contains success and failures, indistinguishable return {'entered': result_num, 'timestamp': timestamp.strftime('%x %X')}
class WifiWEPForm(WifiSTAForm): #: Which security protocol is supported by the form SECURITY = consts.WEP #: WEP supports up to 4 keys to be specified, but only 1 can be active KEY_INDEXES = ( (0, 0), (1, 1), (2, 2), (3, 3), ) #: Validation error messages messages = { 'missing_key': _('The selected key index has no key specified'), } key0 = form.StringField() key1 = form.StringField() key2 = form.StringField() key3 = form.StringField() key_index = form.SelectField(choices=KEY_INDEXES) @classmethod def from_conf_file(cls): """ Initialize the form using configuration file or default config """ ssid = exts.config.get('wireless.ssid', '') key0 = exts.config.get('wireless.key0', '') key1 = exts.config.get('wireless.key1', '') key2 = exts.config.get('wireless.key2', '') key3 = exts.config.get('wireless.key3', '') key_index = exts.config.get('wireless.key_index', '') return cls(data=dict(mode=cls.MODE, security=cls.SECURITY, ssid=ssid, key0=key0, key1=key1, key2=key2, key3=key3, key_index=key_index)) def validate(self): key_index = self.processed_data['key_index'] key = self.processed_data['key{}'.format(key_index)] if not key: raise self.ValidationError('missing_key') # call superclass so saving is executed super(WifiWEPForm, self).validate()
class DateValidator(Validator): """ Validates date fields. This validator attempts to parse the data as date (or date/time) data. When the data cannot be parsed, it fails. :error name(s): date """ messages = { 'date': _('{value} does not look like a valid date'), } def validate(self, value): try: dateutil.parser.parse(value) except (TypeError, ValueError) as exc: raise ValidationError('date', {'value': value, 'exc': str(exc)})
def btmactable(): global DEVICES if request.method == 'POST': # update all bt_macs. if len(request.forms) > 0: for idx, btmac in enumerate(request.forms): #print(btmac) #print(idx, bottle.request.forms.get(btmac), btmac) DEVICES[idx]['bt_mac'] = request.forms.get(btmac) # save directly in DB # db is changed but not the memory data from Server!? if msgDb: msgDb.update_db(account=DEVICES[idx]['account'] , bt_mac=DEVICES[idx]['bt_mac']) return bottle.jinja2_template('btmactable', title=_("BT-Mac Table"), devices=DEVICES)
def post(): ondd_client = request.app.supervisor.exts.ondd is_test_mode = request.forms.get('mode', 'submit') == 'test' ONDDForm = get_form() form = ONDDForm(request.forms) form_valid = form.is_valid() snr_min = request.app.config.get('ondd.snr_min', 0.2) snr_max = request.app.config.get('ondd.snr_max', 0.9) band = ondd_helpers.get_band() if form_valid: # Store full settings logging.info('ONDD: tuner settings updated') ondd_helpers.write_ondd_setup(form.processed_data) if is_test_mode: return dict(successful=False, form=form, status=ondd_client.get_status(), # Translators, shown when tuner settings are # updated during setup wizard step. message=_('Tuner settings have been updated'), band=band, lband=ondd_helpers.LBAND, kuband=ondd_helpers.KUBAND, is_l=band == ondd_helpers.LBAND, is_ku=band == ondd_helpers.KUBAND, preset_keys=ONDDForm.PRESETS[0].values.keys(), SNR_MIN=snr_min, SNR_MAX=snr_max) return dict(successful=True) # Form is not valid if is_test_mode: # We only do something about this in test mode return dict(successful=False, form=form, status=ondd_client.get_status(), band=band, lband=ondd_helpers.LBAND, kuband=ondd_helpers.KUBAND, is_l=band == ondd_helpers.LBAND, is_ku=band == ondd_helpers.KUBAND, preset_keys=ONDDForm.PRESETS[0].values.keys(), SNR_MIN=snr_min, SNR_MAX=snr_max) ondd_helpers.write_ondd_setup({}) return dict(successful=True)
class Username(form.Validator): USERNAME_RE = USERNAME_REGEX messages = { # Translators, error message shown when username does not meet the # requirements during sign-up. 'bad_username': _('Usernames must start with an English letter (a-z) ' 'and may contain up to 12 letters, numbers and an ' "underscore character, '_'") } def validate(self, v): if not v: return if self.USERNAME_RE.match(v.strip()) is None: raise form.ValidationError('bad_username', {'value': v})
def handle_manual_add(): url = request.params.get('url', '').strip() title = request.params.getunicode('title', '').strip() license = request.params.get('license') or None archive = request.params.get('archive') or None errors = {} if not url: # Translators, used as error message on failure to submit content errors['url'] = _('Please type in a valid URL') if not errors: try: content = Content.create(url=url, license=license, title=title, archive=archive) logging.info("Created content for '%s' (real url: '%s')", url, content.url) response.flash(_('Content has been added')) redirect(i18n_path(PREFIX + '/')) except Content.InvalidURLError as err: logging.debug("URL error while parsing '%s': %s", url, err) # Translators, used as error message on failure submit suggestion errors['url'] = _('This URL is invalid') except Content.FetchError as err: logging.debug("Fetch error while parsing '%s': %s (%s)", url, err, err.error) # Translators, used as error message on failure submit suggestion errors['url'] = _('The page at specified URL does not exist') except Content.NotAllowedError as err: logging.debug("Access error while parsing '%s': %s", url, err) # Translators, used as error message on failure submit suggestion errors['url'] = _('The page must be accessible to robots') except Content.ContentError as err: logging.debug("Content error while parsing '%s': %s (%s)", url, err, err.error) # Translators, used as error message on failure submit suggestion errors['url'] = _('The content on the page could not be ' 'understood, please provide and URL to a valid ' 'web page') except Content.BotError as err: logging.exception("Error while fetching '%s': %s", url, err) # Translators, used as error message on failure submit suggestion errors['url'] = _('There was an unknown error with the URL') return get_common_context(dict(vals=request.forms, errors=errors))
class Required(Validator): """ Validates the presence of data. Technically, this validator fails for any data that is an empty string, a string that only contains whitespace, or data that is not a string and evaluates to ``False`` when coerced into boolean (e.g., 0, empty arrays and dicts, etc). :error name(s): required """ messages = { 'required': _('This field is required'), } def validate(self, data): if not data or isinstance(data, basestring) and not data.strip(): # Translator, represents empty field's value raise ValidationError('required', {'value': _('(empty)')})
class WifiSTAForm(WifiForm): #: Used to differentiate between the AP / STA forms in templates MODE = consts.STA_MODE #: Protocol aliases NO_SECURITY = consts.NO_SECURITY WPA_PROTOCOL = consts.WPA WEP_PROTOCOL = consts.WEP #: List of supported security protocols VALID_SECURITY_PROTOCOLS = dict(consts.SECURITY_PROTOCOLS).keys() #: Use this security protocol if no valid one was chosen DEFAULT_SECURITY = WPA_PROTOCOL #: Validation error messages messages = { 'save_error': _('Wireless settings could not be applied'), } ssid = form.StringField(validators=[form.Required()]) security = form.SelectField(choices=consts.SECURITY_PROTOCOLS) def validate(self): """ Perform form-level validation and set the configuration options. """ params = dict(('wireless.{}'.format(key), value) for (key, value) in self.processed_data.items()) try: sta.setup(params) except Exception: logging.exception("Wireless STA settings saving failed.") sta.teardown() raise self.ValidationError('save_error') else: # on successful setup, store persistent config exts.setup.append(params) @classmethod def for_security_protocol(cls, security): if security not in cls.VALID_SECURITY_PROTOCOLS: security = exts.config.get('wireless.security', cls.DEFAULT_SECURITY) cls._subclasses = cls._subclasses or cls.subclasses() (form_cls,) = [sc for sc in cls._subclasses if getattr(sc, 'SECURITY', None) == security] return form_cls
class ONDDFormBase(form.Form): messages = { # Translators, error message shown when a tuner is not detected 'tuning_error': _("Tuner configuration could not be saved. " "Please make sure that the tuner is connected.") } @property def preset_data(self): index = self.processed_data.get('preset', 0) # Negative indexing is valid on lists, so to properly handle when the # custom preset is chosen, it must be checked here if index < 0: return {} try: preset = self.PRESETS[index - 1] except IndexError: return {} else: (_, _, data) = preset return data
def tohtml(self, label=True): s = "" if label and self.type != "hidden": s += "<label>%s</label>\n" % _(self.name) s += "<input type=%s name='%s'" % (self.type, self.name) s += " size='%s'" % self.size s += " id='%s'" % self.name if self.checked: s += " checked" if self.value: s += " value='%s'" % self.value if self.url: s += " data-url='%s'" % self.url if self.path: s += " data-url='%s'" % path(self.path) if self.pick: s += " data-pick='%s'" % json.dumps(self.pick) if self.readonly: s += " readonly" s += ">" return s
def sms(): """SMS messaging page. Let's you select reciepients and message to send to M900 multicell. Returns: web page : Post or Get request answer resulting from the sms template """ global DEVICES if request.method == 'POST': ip = '127.0.0.1' port = 10300 print('init socket...') try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect((ip, port)) s.settimeout(500) except socket.error as exc: print('Caught exception socket.error: {0}'.format(exc)) sys.exit(0) print("init socket successfull") d = json.dumps(request.json).encode("ascii") #print('data:', d) # prepare XML data request xml_message = "<?xml version='1.0' encoding='UTF-8'?> <request version='1.0' type='json-data'><json-data><![CDATA[ {0} ]]></json-data> <jobtype>sms</jobtype> </request>".format(d.decode('ascii')) #print(xml_message) s.send(bytes(xml_message, 'utf-8')) #print('data sent') s.close() else: print('GET request of the page, do nothing') return bottle.jinja2_template('sms', title=_("SMS View"), devices=DEVICES)
class ONDDDashboardPlugin(DashboardPlugin): # Translators, used as dashboard section title heading = _('Tuner Settings') name = 'ondd' priority = 10 def get_template(self): return 'ondd/dashboard' def get_context(self): initial_data = ondd_helpers.read_ondd_setup() preset = match_preset(initial_data) ONDDForm = ondd_forms.FORMS[ondd_helpers.get_band()] band = ondd_helpers.get_band() return dict(band=band, lband=ondd_helpers.LBAND, kuband=ondd_helpers.KUBAND, is_l=band == ondd_helpers.LBAND, is_ku=band == ondd_helpers.KUBAND, preset_keys=ONDDForm.PRESETS[0].values.keys(), form=ONDDForm(initial_data), selected_preset=preset)
class RegistrationForm(form.Form): messages = {'registration_error': _("The entered passwords do not match.")} # Translators, used as label in create user form username = form.StringField(_("Username"), validators=[form.Required()], placeholder=_('username')) # Translators, used as label in create user form password1 = form.PasswordField(_("Password"), validators=[form.Required()], placeholder=_('password')) # Translators, used as label in create user form password2 = form.PasswordField(_("Confirm Password"), validators=[form.Required()], placeholder=_('confirm password')) def validate(self): password1 = self.processed_data['password1'] password2 = self.processed_data['password2'] if password1 != password2: raise form.ValidationError('registration_error', {})
def setup_ondd(): ondd_client = request.app.supervisor.exts.ondd is_test_mode = request.forms.get('mode', 'submit') == 'test' form = ONDDForm(request.forms) form_valid = form.is_valid() snr_min = request.app.config.get('ondd.snr_min', 0.2) snr_max = request.app.config.get('ondd.snr_max', 0.9) if form_valid: # Store full settings logging.info('ONDD: tuner settings updated') request.app.supervisor.exts.setup.append({'ondd': form.processed_data}) if is_test_mode: return dict( successful=False, form=form, status=ondd_client.get_status(), # Translators, shown when tuner settings are updated # during setup wizard step. message=_('Tuner settings have been updated'), SNR_MIN=snr_min, SNR_MAX=snr_max) return dict(successful=True) # Form is not valid if is_test_mode: # We only do something about this in test mode return dict(successful=False, form=form, status=ondd_client.get_status(), SNR_MIN=snr_min, SNR_MAX=snr_max) request.app.supervisor.exts.setup.append({'ondd': {}}) return dict(successful=True)
class PasswordResetForm(form.Form): messages = { 'password_match': _("The entered passwords do not match."), 'invalid_token': _('Password reset token does not match any user'), } # Translators, used as label in create user form reset_token = form.StringField(_("Password reset token"), validators=[form.Required()], placeholder='123456') # Translators, used as label in password reset form password1 = form.PasswordField(_("Password"), validators=[form.Required()], placeholder=_('password')) # Translators, used as label in password reset form password2 = form.PasswordField(_("Confirm Password"), validators=[form.Required()], placeholder=_('confirm password')) def validate(self): password1 = self.processed_data['password1'] password2 = self.processed_data['password2'] if password1 != password2: raise form.ValidationError('password_match', {})
def perform_rebuild(): try: rtime = rebuild() except LockFailureError: logging.debug('DBMANAGE: Global lock could not be acquired') # Translators, error message displayed if database rebuild fails base_msg = _('Database could not be rebuilt. ' 'The following error occurred:') # Translators, error message displayed when locking fails during # database rebuild reason = _('Librarian could not enter maintenance mode and ' 'database rebuild was cancelled. Please make ' 'sure noone else is using Librarian and ' 'try again.') message = ' '.join(map(unicode, [base_msg, reason])) status = 'error' url = i18n_url('dashboard:main') # Translators, redirection target if database backup was successful target = _('Dashboard') else: # Translators, message displayed if database backup was successful base_msg = _('Content database has been rebuilt from scratch. A backup' ' copy of the original database has been created. ' 'You will find it in the files section.') # Translators, message displayed if database backup was successful took_msg = lazy_ngettext('The operation took {time} second', 'The operation took {time} seconds', rtime).format(time=round(rtime, 2)) message = ' '.join(map(unicode, [base_msg, took_msg])) # Translators, message displayed if database backup was successful status = 'success' url = i18n_url(request.app.config['app.default_route']) # Translators, redirection target if database backup was successful target = _('Library') # Translators, used as page title title = _('Database rebuild') return dict(status=status, page_title=title, message=message, redirect_url=url, redirect_target=target)
def delete_path(path): manager = Manager(request.app.supervisor) (success, error) = manager.remove(path) if success: # Translators, used as page title of successful file removal feedback page_title = _("File removed") # Translators, used as message of successful file removal feedback message = _("File successfully removed.") return dict(status='success', page_title=page_title, message=message, redirect_url=get_parent_url(path), redirect_target=_("file list")) # Translators, used as page title of unsuccessful file removal feedback page_title = _("File not removed") # Translators, used as message of unsuccessful file removal feedback message = _("File could not be removed.") return dict(status='error', page_title=page_title, message=message, redirect_url=get_parent_url(path), redirect_target=_("file list"))
from collections import namedtuple from bottle_utils.i18n import lazy_gettext as _ __all__ = ('LBAND', 'KUBAND', 'L_PRESETS', 'KU_PRESETS', 'PRESETS') Preset = namedtuple('Preset', ('label', 'index', 'values')) LBAND = 'l' KUBAND = 'ku' L_PRESETS = [ # Translators, name of the L-band tuner preset covering AF-EU-ME Preset( _('Africa-Europe-Middle East (25E)'), 1, { 'frequency': '1545.94', 'uncertainty': '4000', 'symbolrate': '4200', 'sample_rate': '1', 'rf_filter': '20', 'descrambler': True, # Translators, used as coverage area of a transponder 'coverage': _('Africa, Europe, Middle East'), }), # Translators, name of the L-band tuner preset covering the Americas Preset( _('North and South America (98W)'), 2, {
def tohtml(self): s = "<button id='%s' name='%s'>%s</button>" % ( self.name, self.name, _(self.name)) return s
DELIVERY = ( ('DVB-S', 'DVB-S'), ('DVB-S2', 'DVB-S2'), ) MODULATION = ( ('QPSK', 'QPSK'), ('8PSK', '8PSK'), ('16APSK', '16APSK'), ('32APSK', '32APSK'), ) POLARIZATION = ( # Translators, refers to transponder polarization ('0', _('None')), # Translators, refers to transponder polarization ('v', _('Vertical')), # Translators, refers to transponder polarization ('h', _('Horizontal')), ) VOLTS = { '0': 0, 'v': 13, 'h': 18, } LNB_TYPES = ( # Translators, this is a type of LNB (ondd_consts.UNIVERSAL, _('Universal')),
class KuForm(ONDDFormBase): PRESETS = ondd.KU_PRESETS preset = form.IntegerField( _("Satellite"), validators=[ form.Required(messages={ # Translators, message shown when user does not select a # satellite preset nor 'Custom' option to enter custom data. 'required': _("Please select a satellite or select 'Custom'") }), form.InRangeValidator(min_value=-1, max_value=len(PRESETS)) ] ) # TODO: Add support for DiSEqC azimuth value lnb = form.SelectField( _("LNB Type"), # Translators, error message when LNB type is incorrect validators=[form.Required(messages={ 'required': _('Invalid choice for LNB type') })], choices=ondd.LNB_TYPES ) frequency = form.IntegerField( _("Frequency"), validators=[ form.Required(), form.InRangeValidator( min_value=0, # Translators, error message when frequency value is wrong messages={'min_val': _('Frequency must be a positive number')} ) ] ) symbolrate = form.IntegerField( _("Symbol rate"), validators=[ form.Required(), form.InRangeValidator( min_value=0, # Translators, error message when symbolrate value is wrong messages={'min_val': _('Symbolrate must be a positive number')} ) ] ) delivery = form.SelectField( _("Delivery system"), choices=ondd.DELIVERY, # Translators, error message when wrong delivery system is selected validators=[ form.Required(messages={ 'required': _('Invalid choice for delivery system') }) ] ) modulation = form.SelectField( _("Modulation"), choices=ondd.MODULATION, # Translators, error message when wrong modulation mode is selected validators=[ form.Required(messages={ 'required': _('Invalid choice for modulation mode') }) ] ) polarization = form.SelectField( _("Polarization"), choices=ondd.POLARIZATION, # Translators, error message when wrong polarization is selected validators=[ form.Required(messages={ 'required': _('Invalid choice for polarization') }) ] ) def preprocess_frequency(self, value): return self.preset_data.get('frequency', value) def preprocess_symbolrate(self, value): return self.preset_data.get('symbolrate', value) def preprocess_delivery(self, value): return self.preset_data.get('delivery', value) def preprocess_modulation(self, value): return self.preset_data.get('modulation', value) def preprocess_polarization(self, value): return self.preset_data.get('polarization', value) def validate(self): if not ondd.has_tuner(): # Translators, error message shown when a tuner is not detected raise form.ValidationError('tuning_error', {}) lnb = self.processed_data['lnb'] frequency = self.processed_data['frequency'] symbolrate = self.processed_data['symbolrate'] delivery = self.processed_data['delivery'] modulation = self.processed_data['modulation'] polarization = self.processed_data['polarization'] settings = dict(frequency=freq_conv(frequency, lnb), symbolrate=symbolrate, delivery=delivery, tone=needs_tone(frequency, lnb), modulation=dict(ondd.MODULATION)[modulation], voltage=ondd.VOLTS[polarization]) ondd_client = request.app.supervisor.exts.ondd response = ondd_client.set_settings(**settings) if not response.startswith('2'): # Translators, error message shown when setting transponder # configuration is not successful raise form.ValidationError('tuning_error', {}) ondd.write_ondd_setup(self.processed_data)
class Field(Ordered, ErrorMixin): """ Form field base class. This class provides the base functionality for all form fields. The ``label`` argument is used to specify the field's label. The ``validators`` argument is used to specify the validators that will be used on the field data. If any data should be bound to a field, the ``value`` argument can be used to specify it. Value can be a callable, in which case it is called and its return value used as ``value``. The ``name`` argument is used to specify the field name. The ``messages`` argument is used to customize the validation error messages. These override any messages found in the :py:attr:`~messages` attribute. Any extra keyword attributes passed to the constructor are stored as :py:attr:`~options` property on the instance. """ ValidationError = ValidationError #: Field ID attribute prefix (this is prepended to the field name) _id_prefix = '' #: Generic error message to be used when no messages match the validation #: error. # Translators, used as generic error message in form fields, 'value' should # not be translated. generic_error = _('Invalid value for this field') #: Validation error messages. messages = {} #: Field markup type. This is arbitrary and normally used in the templates #: to differentiate between field types. It is up to the template author to #: decide how this should be treated. type = 'text' def __new__(cls, *args, **kwargs): if 'name' in kwargs: return super(Field, cls).__new__(cls) return DormantField(cls, args, kwargs) def __init__(self, label=None, validators=None, value=None, name=None, messages={}, **options): super(Field, self).__init__() # needed to apply ordering #: Field name self.name = name #: Field label self.label = label #: Field validators self.validators = validators or [] #: Raw value of the field self.value = value() if callable(value) else value #: Processed value of the field self.processed_value = None #: Whether value is bound to this field self.is_value_bound = False self._error = None #: Extra keyword argument passed to the constructor self.options = options # Copy the messages so that modifying them does not result in class' # ``messages`` attribute to be altered. self.messages = self.messages.copy() # Collect default messages from all validators into the messages dict for validator in self.validators: self.messages.update(validator.messages) # Update the messages dict with any user-supplied messages self.messages.update(messages) def bind_value(self, value): """ Binds a value. This method also sets the :py:attr:`~is_value_bound` property to ``True``. """ self.value = value self.is_value_bound = True def is_valid(self): """ Validate form field and return ``True`` is data is valid. If there is an error during Validation, the error object is stored in the :py:attr:`~_error` property. Before validation, the raw value is processed using the :py:meth:`~parse` method, and stored in :py:attr:`~processed_value` attribute. When parsing fails with ``ValueError`` exception, a 'generic' error is stored. The default message for this error is stored in the :py:attr:`~generic_error` property, and can be customized by passing a ``'generic'`` message as part of the ``messages`` constructor argument. """ try: self.processed_value = self.parse(self.processed_value) except ValueError as exc: self._error = ValidationError('generic', {'value': self.processed_value}) return False for validate in self.validators: try: validate(self.processed_value) except ValidationError as exc: self._error = exc return False return True def parse(self, value): """ Parse the raw value and convert to Python object. Subclasses should return the value in it's correct type. In case the passed in value cannot be cast into it's correct type, the method should raise a ``ValueError`` exception with an appropriate error message. """ raise NotImplementedError()
class DashboardMenuItem(MenuItem): name = 'dashboard' label = _("Settings") icon_class = 'settings' route = 'dashboard:main'
class LForm(ONDDFormBase): PRESETS = ondd.L_PRESETS preset = form.IntegerField( _("Satellite"), validators=[ form.Required(messages={ # Translators, message shown when user does not select a # satellite preset nor 'Custom' option to enter custom data. 'required': _("Please select a satellite or select 'Custom'") }), form.InRangeValidator(min_value=-1, max_value=len(PRESETS)) ] ) frequency = form.FloatField( _("Frequency"), validators=[ form.Required(), form.InRangeValidator( min_value=0, # Translators, error message when frequency value is wrong messages={'min_val': _('Frequency must be a positive number')} ) ] ) uncertainty = form.IntegerField( _("Frequency uncertainty"), validators=[ form.Required(), form.InRangeValidator( min_value=0, # Translators, error message when uncertainty value is wrong messages={'min_val': _('Frequency uncertainty must be a ' 'positive number')} ) ] ) symbolrate = form.IntegerField( _("Symbol rate"), validators=[ form.Required(), form.InRangeValidator( min_value=0, # Translators, error message when symbolrate value is wrong messages={'min_val': _('Symbolrate must be a positive number')} ) ] ) sample_rate = form.FloatField( _("Sample rate"), validators=[ form.Required(), form.InRangeValidator( min_value=0, # Translators, error message when sample rate is wrong messages={ 'min_val': _('Sample rate must be a positive number') } ) ] ) rf_filter = form.SelectField( _("RF filter"), # Translators, error message when LNB type is incorrect choices=ondd.RF_FILTERS ) descrambler = form.BooleanField( _("Descrambler"), value='descrambler', default=False, ) def preprocess_frequency(self, value): return self.preset_data.get('frequency', value) def preprocess_uncertainty(self, value): return self.preset_data.get('uncertainty', value) def preprocess_symbolrate(self, value): return self.preset_data.get('symbolrate', value) def preprocess_sample_rate(self, value): return self.preset_data.get('sample_rate', value) def preprocess_rf_filter(self, value): return int(self.preset_data.get('rf_filter', value)) def preprocess_descrambler(self, value): return self.preset_data.get('descrambler', value) def validate(self): ondd.write_ondd_setup(self.processed_data) try: ondd.restart_demod() except ondd.DemodRestartError: raise form.ValidationError('tuning_error', {})
class ONDDForm(form.Form): PRESETS = consts.PRESETS messages = { 'tuning_error': _("Tuner configuration could not be saved. " "Please make sure that the tuner is connected.") } # TODO: Add support for DiSEqC azimuth value lnb = form.SelectField( _("LNB Type"), # Translators, error message when LNB type is incorrect validators=[ form.Required( messages={'required': _('Invalid choice for LNB type')}) ], choices=consts.LNB_TYPES) frequency = form.IntegerField( _("Frequency"), validators=[ form.Required(), form.InRangeValidator( min_value=0, # Translators, error message when frequency value is wrong messages={'min_val': _('Frequency must be a positive number')}) ]) symbolrate = form.IntegerField( _("Symbol rate"), validators=[ form.Required(), form.InRangeValidator( min_value=0, # Translators, error message when symbolrate value is wrong messages={ 'min_val': _('Symbolrate must be a positive number') }) ]) delivery = form.SelectField( _("Delivery system"), choices=consts.DELIVERY, # Translators, error message when wrong delivery system is selected validators=[ form.Required( messages={'required': _('Invalid choice for delivery system')}) ]) modulation = form.SelectField( _("Modulation"), choices=consts.MODULATION, # Translators, error message when wrong modulation mode is selected validators=[ form.Required( messages={'required': _('Invalid choice for modulation mode')}) ]) polarization = form.SelectField( _("Polarization"), choices=consts.POLARIZATION, # Translators, error message when wrong polarization is selected validators=[ form.Required( messages={'required': _('Invalid choice for polarization')}) ]) def validate(self): if not has_tuner(): # Translators, error message shown when a tuner is not detected raise form.ValidationError('tuning_error', {}) lnb = self.processed_data['lnb'] frequency = self.processed_data['frequency'] symbolrate = self.processed_data['symbolrate'] delivery = self.processed_data['delivery'] modulation = self.processed_data['modulation'] polarization = self.processed_data['polarization'] settings = dict(frequency=freq_conv(frequency, lnb), symbolrate=symbolrate, delivery=delivery, tone=needs_tone(frequency, lnb), modulation=dict(consts.MODULATION)[modulation], voltage=consts.VOLTS[polarization]) ondd_client = request.app.supervisor.exts.ondd response = ondd_client.set_settings(**settings) if not response.startswith('2'): # Translators, error message shown when setting transponder # configuration is not successful raise form.ValidationError('tuning_error', {})