def test_durationfield_clean(self): f = DurationField() self.assertEqual(datetime.timedelta(seconds=30), f.clean('30')) self.assertEqual(datetime.timedelta(minutes=15, seconds=30), f.clean('15:30')) self.assertEqual(datetime.timedelta(hours=1, minutes=15, seconds=30), f.clean('1:15:30')) self.assertEqual( datetime.timedelta(days=1, hours=1, minutes=15, seconds=30, milliseconds=300), f.clean('1 1:15:30.3') )
def test_overflow(self): msg = "The number of days must be between {min_days} and {max_days}.".format( min_days=datetime.timedelta.min.days, max_days=datetime.timedelta.max.days, ) f = DurationField() with self.assertRaisesMessage(ValidationError, msg): f.clean('1000000000 00:00:00') with self.assertRaisesMessage(ValidationError, msg): f.clean('-1000000000 00:00:00')
def test_durationfield_clean(self): f = DurationField() self.assertEqual(datetime.timedelta(seconds=30), f.clean('30')) self.assertEqual(datetime.timedelta(minutes=15, seconds=30), f.clean('15:30')) self.assertEqual(datetime.timedelta(hours=1, minutes=15, seconds=30), f.clean('1:15:30')) self.assertEqual( datetime.timedelta(days=1, hours=1, minutes=15, seconds=30, milliseconds=300), f.clean('1 1:15:30.3'))
class EventForm(Form): name = forms.CharField(label='Nom de l\'évènement', max_length=100, required=True) location = forms.CharField(label='Emplacement', max_length=255, required=False) event_date_start = SplitDateTimeField( label='Date de début de l\'évènement', widget=DateTimePickerWidget, initial=timezone.now()) event_duration = DurationField(initial=timedelta(hours=1), label="Durée de l\'évènement") banner = forms.ImageField(required=False, label='Bannière', widget=ImageSelectorWidget) description = forms.CharField( label='Description', widget=MarkdownWidget, required=True, initial=render_to_string("event_description_template.md"))
class ScriptForm(ModelForm): script_type = CharField( label="Script type", required=False, help_text="Script type", initial=str(SCRIPT_TYPE.TESTING), ) hardware_type = CharField( label="Hardware type", required=False, help_text="The hardware type the script configures or tests.", initial=str(HARDWARE_TYPE.NODE), ) parallel = CharField( label="Parallel", required=False, help_text="Whether the script may run in parallel with other scripts.", initial=str(SCRIPT_PARALLEL.DISABLED), ) packages = CharField( label="Packages", required=False, help_text="Packages to be installed with script.", initial="", ) timeout = DurationField( label="Timeout", required=False, help_text="Timeout", initial=timedelta(0), ) script = VersionedTextFileField(label="Script", help_text="Script content") comment = CharField( label="Comment", required=False, help_text="Description of change", initial="", ) for_hardware = CharField( label="For hardware", required=False, help_text="Hardware identifiers this script requires to run.", initial="", ) apply_configured_networking = BooleanField(required=False) class Meta: model = Script fields = ( "name", "title", "description", "tags", "script_type", "hardware_type", "parallel", "packages", "timeout", "destructive", "script", "for_hardware", "may_reboot", "recommission", "apply_configured_networking", ) def __init__(self, instance=None, data=None, edit_default=False, **kwargs): self.edit_default = edit_default if instance is None: script_data_key = "data" else: script_data_key = "new_data" data = data.copy() if "comment" in data and "script" in data: script_data = { "comment": data.get("comment"), script_data_key: data.get("script"), } data["script"] = script_data data.pop("comment") # Alias type to script_type to allow for consistent naming in the API. if "type" in data and "script_type" not in data: data["script_type"] = data["type"] # self.data is a QueryDict. pop returns a list containing the value # while directly accessing it returns just the value. data.pop("type") super().__init__(instance=instance, data=data, **kwargs) if instance is None: for field in ["name", "script"]: self.fields[field].required = True else: for field in ["name", "script"]: self.fields[field].required = False self.fields["script"].initial = instance.script # Reading the embedded YAML must happen at the end of initialization # so the fields set are validated. if "script" in self.data: self._read_script() def _validate_results(self, results={}): valid = True if isinstance(results, list): for result in results: if not isinstance(result, str): set_form_error( self, "results", "Each result in a result list must be a string.", ) valid = False elif isinstance(results, dict): for result in results.values(): if not isinstance(result, dict): set_form_error( self, "results", "Each result in a result dictionary must be a " "dictionary.", ) elif "title" not in result: set_form_error( self, "results", "title must be included in a result dictionary.", ) valid = False else: for key in ["title", "description"]: if key in result and not isinstance(result[key], str): set_form_error(self, "results", "%s must be a string." % key) valid = False else: set_form_error( self, "results", "results must be a list of strings or a dictionary of " "dictionaries.", ) valid = False return valid def _clean_script(self, parsed_yaml): """Clean script data and validate input.""" # Tags and timeout may not be updated from new embedded YAML. This # allows users to receive updated scripts from an upstream maintainer, # such as Canonical, while maintaining user defined tags and timeout. # Tags must be a comma seperated string for the form. tags = parsed_yaml.pop("tags", None) if (tags is not None and self.instance.id is None and "tags" not in self.data): tags_valid = True if isinstance(tags, str): self.data["tags"] = tags elif isinstance(tags, list): for tag in tags: if not isinstance(tag, str): tags_valid = False continue if tags_valid: self.data["tags"] = ",".join(tags) else: tags_valid = False if not tags_valid: set_form_error( self, "tags", "Embedded tags must be a string of comma seperated " "values, or a list of strings.", ) # Timeout must be a string for the form. timeout = parsed_yaml.pop("timeout", None) if (timeout is not None and self.instance.id is None and "timeout" not in self.data): self.data["timeout"] = str(timeout) # Packages and for_hardware must be a JSON string for the form. for key in ["packages", "for_hardware"]: value = parsed_yaml.pop(key, None) if value is not None and key not in self.data: self.data[key] = json.dumps(value) for key, value in parsed_yaml.items(): if key in self.fields: error = False if key not in self.data: self.data[key] = value elif key == "script_type": # The deprecated Commissioning API always sets the # script_type to commissioning as it has always only # accepted commissioning scripts while the form sets # the default type to testing. If the YAML matches the # type allow it. try: if translate_script_type( value) != translate_script_type( self.data[key]): error = True except ValidationError: error = True elif value != self.data[key]: # Only allow form data for fields defined in the YAML if # the data matches. error = True if error: set_form_error( self, key, "May not override values defined in embedded YAML.", ) def _read_script(self): """Read embedded YAML configuration in a script. Search for supported MAAS script metadata in the script and read the values. Leading '#' are ignored. If the values are fields they will be entered in the form. """ yaml_delim = re.compile( r"\s*#\s*-+\s*(Start|End) MAAS (?P<version>\d+\.\d+) " r"script metadata\s+-+", re.I, ) found_version = None yaml_content = "" if isinstance(self.data["script"], dict): if "new_data" in self.data["script"]: script = self.data["script"]["new_data"] else: script = self.data["script"]["data"] else: script = self.data["script"] script_splitlines = script.splitlines() if len(script_splitlines) >= 1 and not script_splitlines[0].startswith( "#!/"): set_form_error(self, "script", "Must start with shebang.") for line in script_splitlines[1:]: m = yaml_delim.search(line) if m is not None: if found_version is None and m.group("version") == "1.0": # Found the start of the embedded YAML found_version = m.group("version") continue elif found_version == m.group("version"): # Found the end of the embedded YAML break elif found_version is not None and line.strip() != "": # Capture all lines inbetween the deliminator if "#" not in line: set_form_error(self, "script", 'Missing "#" on YAML line.') return yaml_content += "%s\n" % line.split("#", 1)[1] try: parsed_yaml = yaml.safe_load(yaml_content) except yaml.YAMLError as err: set_form_error(self, "script", "Invalid YAML: %s" % err) return if not isinstance(parsed_yaml, dict): return self.instance.results = parsed_yaml.pop("results", {}) self.instance.parameters = parsed_yaml.pop("parameters", {}) self._clean_script(parsed_yaml) def clean_packages(self): if self.cleaned_data["packages"] == "": return self.instance.packages else: packages = json.loads(self.cleaned_data["packages"]) # Automatically convert into a list incase only one package is # needed. for key in ["apt", "snap", "url"]: if key in packages and not isinstance(packages[key], list): packages[key] = [packages[key]] for key in ["apt", "url"]: if key in packages: for package in packages[key]: if not isinstance(package, str): set_form_error( self, "packages", "Each %s package must be a string." % key, ) if "snap" in packages: for package in packages["snap"]: if isinstance(package, dict): if "name" not in package or not isinstance( package["name"], str): set_form_error( self, "packages", "Snap package name must be defined.", ) if "channel" in package and package["channel"] not in [ "stable", "edge", "beta", "candidate", ]: set_form_error( self, "packages", "Snap channel must be stable, edge, beta, " "or candidate.", ) if "mode" in package and package["mode"] not in [ "classic", "dev", "jail", ]: set_form_error( self, "packages", "Snap mode must be classic, dev, or jail.", ) elif not isinstance(package, str): set_form_error(self, "packages", "Snap package must be a string.") return packages def clean_for_hardware(self): """Convert from JSON and validate for_hardware input.""" if self.cleaned_data["for_hardware"] == "": return self.instance.for_hardware try: for_hardware = json.loads(self.cleaned_data["for_hardware"]) except JSONDecodeError: for_hardware = self.cleaned_data["for_hardware"] if isinstance(for_hardware, str): for_hardware = for_hardware.split(",") if not isinstance(for_hardware, list): set_form_error(self, "for_hardware", "Must be a list or string") return regex = re.compile( r"^modalias:.+|pci:[\da-f]{4}:[\da-f]{4}|" r"usb:[\da-f]{4}:[\da-f]{4}|" r"system_vendor:.*|" r"system_product:.*|" r"system_version:.*|" r"mainboard_vendor:.*|" r"mainboard_product:.*$", re.I, ) for hw_id in for_hardware: if regex.search(hw_id) is None: set_form_error( self, "for_hardware", "Hardware identifier '%s' must be a modalias, PCI ID, " "USB ID, system vendor, system product, system version, " "mainboard vendor, or mainboard product." % hw_id, ) return for_hardware def clean(self): cleaned_data = super().clean() # If a field wasn't passed in keep the old values when updating. if self.instance.id is not None: for field in self._meta.fields: if field not in self.data: cleaned_data[field] = getattr(self.instance, field) script_type = cleaned_data["script_type"] if script_type == "": cleaned_data["script_type"] = self.instance.script_type else: try: cleaned_data["script_type"] = translate_script_type( script_type) except ValidationError as e: set_form_error(self, "script_type", e) hardware_type = cleaned_data["hardware_type"] if hardware_type == "": cleaned_data["hardware_type"] = self.instance.hardware_type else: try: cleaned_data["hardware_type"] = translate_hardware_type( hardware_type) except ValidationError as e: set_form_error(self, "hardware_type", e) parallel = cleaned_data["parallel"] if parallel == "": cleaned_data["parallel"] = self.instance.parallel else: try: cleaned_data["parallel"] = translate_script_parallel(parallel) except ValidationError as e: set_form_error(self, "parallel", e) return cleaned_data def is_valid(self): valid = super().is_valid() if valid and self.instance.default and not self.edit_default: for field in self.Meta.fields: if field in ["tags", "timeout"]: continue if field in self.data: set_form_error( self, field, "Not allowed to change on default scripts.", ) valid = False name = self.data.get("name") # none is used to tell the API to not run testing_scripts during # commissioning. if name is not None and name.lower() == "none": set_form_error(self, "name", '"none" is a reserved name.') valid = False # The name can't be a digit as MAAS allows scripts to be selected by # id. if name is not None and name.isdigit(): set_form_error(self, "name", "Cannot be a number.") valid = False if name is not None and pipes.quote(name) != name: set_form_error( self, "name", "Name contains disallowed characters, e.g. space or quotes.", ) valid = False # If comment and script exist __init__ combines both fields into a dict # to pass to VersionedTextFileField. if "comment" in self.data: set_form_error( self, "comment", '"comment" may only be used when specifying a "script" ' "as well.", ) valid = False if "script" in self.data: if not self._validate_results(self.instance.results): valid = False if "parameters" in self.data: params_form = ParametersForm(data=self.data.get("parameters")) if not params_form.is_valid(): valid = False if (not valid and self.instance.script_id is not None and self.initial.get("script") != self.instance.script_id and self.instance.script.id is not None): # If form validation failed cleanup any new VersionedTextFile # created by the VersionedTextFileField. self.instance.script.delete() return valid def save(self, *args, **kwargs): request = kwargs.pop("request", None) endpoint = kwargs.pop("endpoint", None) script = super(ScriptForm, self).save(*args, **kwargs) # Create audit event log if endpoint and request supplied. if request is not None and endpoint is not None: create_audit_event( EVENT_TYPES.SETTINGS, endpoint, request, None, description="Saved script '%s'." % script.name, ) return script
class ScriptForm(ModelForm): script_type = CharField(label='Script type', required=False, help_text='Script type', initial=str(SCRIPT_TYPE.TESTING)) hardware_type = CharField( label='Hardware type', required=False, help_text='The hardware type the script configures or tests.', initial=str(HARDWARE_TYPE.NODE)) parallel = CharField( label='Parallel', required=False, help_text='Whether the script may run in parallel with other scripts.', initial=str(SCRIPT_PARALLEL.DISABLED)) packages = CharField(label='Packages', required=False, help_text='Packages to be installed with script.', initial='') timeout = DurationField(label='Timeout', required=False, help_text='Timeout', initial=timedelta(0)) script = VersionedTextFileField(label='Script', help_text='Script content') comment = CharField(label='Comment', required=False, help_text='Description of change', initial='') for_hardware = CharField( label='For hardware', required=False, help_text='Hardware identifiers this script requires to run.', initial='') class Meta: model = Script fields = ( 'name', 'title', 'description', 'tags', 'script_type', 'hardware_type', 'parallel', 'packages', 'timeout', 'destructive', 'script', 'for_hardware', 'may_reboot', 'recommission', ) def __init__(self, instance=None, data=None, edit_default=False, **kwargs): self.edit_default = edit_default if instance is None: script_data_key = 'data' else: script_data_key = 'new_data' data = data.copy() if 'comment' in data and 'script' in data: script_data = { 'comment': data.get('comment'), script_data_key: data.get('script'), } data['script'] = script_data data.pop('comment') # Alias type to script_type to allow for consistent naming in the API. if 'type' in data and 'script_type' not in data: data['script_type'] = data['type'] # self.data is a QueryDict. pop returns a list containing the value # while directly accessing it returns just the value. data.pop('type') super().__init__(instance=instance, data=data, **kwargs) if instance is None: for field in ['name', 'script']: self.fields[field].required = True else: for field in ['name', 'script']: self.fields[field].required = False self.fields['script'].initial = instance.script # Reading the embedded YAML must happen at the end of initialization # so the fields set are validated. if 'script' in self.data: self._read_script() def _validate_results(self, results={}): valid = True if isinstance(results, list): for result in results: if not isinstance(result, str): set_form_error( self, 'results', 'Each result in a result list must be a string.') valid = False elif isinstance(results, dict): for result in results.values(): if not isinstance(result, dict): set_form_error( self, 'results', 'Each result in a result dictionary must be a ' 'dictionary.') elif 'title' not in result: set_form_error( self, 'results', 'title must be included in a result dictionary.') valid = False else: for key in ['title', 'description']: if key in result and not isinstance(result[key], str): set_form_error(self, 'results', '%s must be a string.' % key) valid = False else: set_form_error( self, 'results', 'results must be a list of strings or a dictionary of ' 'dictionaries.') valid = False return valid def _read_script(self): """Read embedded YAML configuration in a script. Search for supported MAAS script metadata in the script and read the values. Leading '#' are ignored. If the values are fields they will be entered in the form. """ yaml_delim = re.compile( '\s*#\s*-+\s*(Start|End) MAAS (?P<version>\d+\.\d+) ' 'script metadata\s+-+', re.I) found_version = None yaml_content = '' if isinstance(self.data['script'], dict): if 'new_data' in self.data['script']: script = self.data['script']['new_data'] else: script = self.data['script']['data'] else: script = self.data['script'] script_splitlines = script.splitlines() if (len(script_splitlines) >= 1 and not script_splitlines[0].startswith('#!/')): set_form_error(self, 'script', 'Must start with shebang.') for line in script_splitlines[1:]: m = yaml_delim.search(line) if m is not None: if found_version is None and m.group('version') == '1.0': # Found the start of the embedded YAML found_version = m.group('version') continue elif found_version == m.group('version'): # Found the end of the embedded YAML break elif found_version is not None and line.strip() != '': # Capture all lines inbetween the deliminator if '#' not in line: set_form_error(self, 'script', 'Missing "#" on YAML line.') return yaml_content += '%s\n' % line.split('#', 1)[1] try: parsed_yaml = yaml.safe_load(yaml_content) except yaml.YAMLError as err: set_form_error(self, 'script', 'Invalid YAML: %s' % err) return if not isinstance(parsed_yaml, dict): return self.instance.results = parsed_yaml.pop('results', {}) self.instance.parameters = parsed_yaml.pop('parameters', {}) # Tags and timeout may not be updated from new embedded YAML. This # allows users to receive updated scripts from an upstream maintainer, # such as Canonical, while maintaining user defined tags and timeout. # Tags must be a comma seperated string for the form. tags = parsed_yaml.pop('tags', None) if (tags is not None and self.instance.id is None and 'tags' not in self.data): tags_valid = True if isinstance(tags, str): self.data['tags'] = tags elif isinstance(tags, list): for tag in tags: if not isinstance(tag, str): tags_valid = False continue if tags_valid: self.data['tags'] = ','.join(tags) else: tags_valid = False if not tags_valid: set_form_error( self, 'tags', 'Embedded tags must be a string of comma seperated ' 'values, or a list of strings.') # Timeout must be a string for the form. timeout = parsed_yaml.pop('timeout', None) if (timeout is not None and self.instance.id is None and 'timeout' not in self.data): self.data['timeout'] = str(timeout) # Packages and for_hardware must be a JSON string for the form. for key in ['packages', 'for_hardware']: value = parsed_yaml.pop(key, None) if value is not None and key not in self.data: self.data[key] = json.dumps(value) for key, value in parsed_yaml.items(): if key in self.fields: if key not in self.data: self.data[key] = value else: set_form_error( self, key, 'May not override values defined in embedded YAML.') def clean_packages(self): if self.cleaned_data['packages'] == '': return self.instance.packages else: packages = json.loads(self.cleaned_data['packages']) # Automatically convert into a list incase only one package is # needed. for key in ['apt', 'snap', 'url']: if key in packages and not isinstance(packages[key], list): packages[key] = [packages[key]] for key in ['apt', 'url']: if key in packages: for package in packages[key]: if not isinstance(package, str): set_form_error( self, 'packages', 'Each %s package must be a string.' % key) if 'snap' in packages: for package in packages['snap']: if isinstance(package, dict): if ('name' not in package or not isinstance(package['name'], str)): set_form_error( self, 'packages', 'Snap package name must be defined.') if ('channel' in package and package['channel'] not in [ 'stable', 'edge', 'beta', 'candidate' ]): set_form_error( self, 'packages', 'Snap channel must be stable, edge, beta, ' 'or candidate.') if ('mode' in package and package['mode'] not in ['classic', 'dev', 'jail']): set_form_error( self, 'packages', 'Snap mode must be classic, dev, or jail.') elif not isinstance(package, str): set_form_error(self, 'packages', 'Snap package must be a string.') return packages def clean_for_hardware(self): """Convert from JSON and validate for_hardware input.""" if self.cleaned_data['for_hardware'] == '': return self.instance.for_hardware try: for_hardware = json.loads(self.cleaned_data['for_hardware']) except JSONDecodeError: for_hardware = self.cleaned_data['for_hardware'] if isinstance(for_hardware, str): for_hardware = for_hardware.split(',') if not isinstance(for_hardware, list): set_form_error(self, 'for_hardware', 'Must be a list or string') return regex = re.compile( '^modalias:.+|pci:[\da-f]{4}:[\da-f]{4}|' 'usb:[\da-f]{4}:[\da-f]{4}|' 'system_vendor:.*|' 'system_product:.*|' 'system_version:.*|' 'mainboard_vendor:.*|' 'mainboard_product:.*$', re.I) for hw_id in for_hardware: if regex.search(hw_id) is None: set_form_error( self, 'for_hardware', "Hardware identifier '%s' must be a modalias, PCI ID, " "USB ID, system vendor, system product, system version, " "mainboard vendor, or mainboard product." % hw_id) return for_hardware def clean(self): cleaned_data = super().clean() # If a field wasn't passed in keep the old values when updating. if self.instance.id is not None: for field in self._meta.fields: if field not in self.data: cleaned_data[field] = getattr(self.instance, field) script_type = cleaned_data['script_type'] if script_type == '': cleaned_data['script_type'] = self.instance.script_type else: try: cleaned_data['script_type'] = translate_script_type( script_type) except ValidationError as e: set_form_error(self, 'script_type', e) hardware_type = cleaned_data['hardware_type'] if hardware_type == '': cleaned_data['hardware_type'] = self.instance.hardware_type else: try: cleaned_data['hardware_type'] = translate_hardware_type( hardware_type) except ValidationError as e: set_form_error(self, 'hardware_type', e) parallel = cleaned_data['parallel'] if parallel == '': cleaned_data['parallel'] = self.instance.parallel else: try: cleaned_data['parallel'] = translate_script_parallel(parallel) except ValidationError as e: set_form_error(self, 'parallel', e) return cleaned_data def is_valid(self): valid = super().is_valid() if valid and self.instance.default and not self.edit_default: for field in self.Meta.fields: if field in ['tags', 'timeout']: continue if field in self.data: set_form_error( self, field, 'Not allowed to change on default scripts.') valid = False name = self.data.get('name') # none is used to tell the API to not run testing_scripts during # commissioning. if name is not None and name.lower() == 'none': set_form_error(self, 'name', '"none" is a reserved name.') valid = False # The name can't be a digit as MAAS allows scripts to be selected by # id. if name is not None and name.isdigit(): set_form_error(self, 'name', 'Cannot be a number.') valid = False if name is not None and pipes.quote(name) != name: set_form_error( self, 'name', 'Name contains disallowed characters, e.g. space or quotes.') valid = False # If comment and script exist __init__ combines both fields into a dict # to pass to VersionedTextFileField. if 'comment' in self.data: set_form_error( self, 'comment', '"comment" may only be used when specifying a "script" ' 'as well.') valid = False if 'script' in self.data: if not self._validate_results(self.instance.results): valid = False if 'parameters' in self.data: params_form = ParametersForm(data=self.data.get('parameters')) if not params_form.is_valid(): valid = False if (not valid and self.instance.script_id is not None and self.initial.get('script') != self.instance.script_id and self.instance.script.id is not None): # If form validation failed cleanup any new VersionedTextFile # created by the VersionedTextFileField. self.instance.script.delete() return valid def save(self, *args, **kwargs): request = kwargs.pop('request', None) endpoint = kwargs.pop('endpoint', None) script = super(ScriptForm, self).save(*args, **kwargs) # Create audit event log if endpoint and request supplied. if request is not None and endpoint is not None: create_audit_event(EVENT_TYPES.SETTINGS, endpoint, request, None, description=("Script %s" % script.name + " saved for '%(username)s'.")) return script
class ActEditDraftForm(ModelForm): required_css_class = 'required' error_css_class = 'error' act_duration = DurationField(required=False, help_text=act_help_texts['act_duration']) track_artist = CharField(required=False) track_title = CharField(required=False) shows_preferences = MultipleChoiceField( widget=CheckboxSelectMultiple, choices=act_shows_options, label=act_bid_labels['shows_preferences'], help_text=act_help_texts['shows_preferences'], required=False) other_performance = MultipleChoiceField( widget=CheckboxSelectMultiple, choices=act_other_perf_options, label=act_bid_labels['other_performance'], help_text=act_help_texts['other_performance'], required=False) video_link = URLField(widget=URLInput(attrs={'placeholder': 'http://'}), help_text=act_help_texts['video_link'], label=act_bid_labels['video_link'], required=False) b_conference = HiddenInput() b_description = CharField(required=True, label=act_bid_labels['description'], help_text=act_help_texts['description'], widget=Textarea) class Meta: model = Act fields = [ 'performer', 'shows_preferences', 'other_performance', 'b_title', 'track_title', 'track_artist', 'act_duration', 'video_link', 'video_choice', 'b_description', 'why_you', 'b_conference' ] labels = act_bid_labels help_texts = act_help_texts widgets = { 'b_conference': HiddenInput(), 'performer': AddAnotherEditSelectedWidgetWrapper( autocomplete.ModelSelect2( url='limited-performer-autocomplete'), reverse_lazy('persona-add', urlconf='gbe.urls', args=[1]), reverse_lazy('performer-update', urlconf='gbe.urls', args=['__fk__'])), } def __init__(self, *args, **kwargs): super(ActEditDraftForm, self).__init__(*args, **kwargs) if self.instance: obj_data = self.instance.__dict__ if obj_data['shows_preferences']: self.initial['shows_preferences'] = jsonify( obj_data['shows_preferences']) if obj_data['other_performance']: self.initial['other_performance'] = jsonify( obj_data['other_performance'])
def test_durationfield_prepare_value(self): field = DurationField() td = datetime.timedelta(minutes=15, seconds=30) self.assertEqual(field.prepare_value(td), duration_string(td)) self.assertEqual(field.prepare_value('arbitrary'), 'arbitrary') self.assertIsNone(field.prepare_value(None))
def test_durationfield_integer_value(self): f = DurationField() self.assertEqual(datetime.timedelta(0, 10800), f.clean(10800))
def test_durationfield_render(self): self.assertWidgetRendersTo( DurationField(initial=datetime.timedelta(hours=1)), '<input id="id_f" type="text" name="f" value="01:00:00" required>', )
class TaskForm(ModelForm): model = Task duration = DurationField(widget=TimeDurationWidget(show_days=True, show_hours=True, show_minutes=True, show_seconds=False), required=False) start_date = CharField(widget=TextInput(attrs={'type': 'date'})) end_date = CharField(widget=TextInput(attrs={'type': 'date'})) def __init__(self, *args, **kwargs): user = kwargs.pop('user') super().__init__(*args, **kwargs) instance = getattr(self, 'instance', None) # tags self.fields['tags'].widget.attrs['multiple'] = 'multiple' self.fields['tags'].widget.attrs['class'] = "selectpicker" self.fields['tags'].queryset = user.tags.all() l_tags_dict = OrderedDict() tags_in_project = [] if instance.pk: tags_in_project = instance.tags.all() for t in tags_in_project: try: l_tags_dict['in the project'].append((t.pk, t.name)) except KeyError: l_tags_dict['in the project'] = [(t.pk, t.name)] for t in set(instance.refer_client.tags.all()).difference( tags_in_project): try: l_tags_dict['not yet in the project'].append( (t.pk, t.name)) except KeyError: l_tags_dict['not yet in the project'] = [(t.pk, t.name)] self.fields['tags'].choices = l_tags_dict.items() else: self.fields['tags'].queryset = user.tags.all() # users self.fields['users'].widget.attrs['multiple'] = 'multiple' self.fields['users'].widget.attrs['class'] = "selectpicker" l_users_dict = OrderedDict() users_in_project = [] if instance.pk: users_in_project = instance.users.all() for u in users_in_project: try: l_users_dict['in the project'].append((u.pk, u.name)) except KeyError: l_users_dict['in the project'] = [(u.pk, u.name)] for u in set(instance.refer_client.users.all()).difference( users_in_project): try: l_users_dict['not yet in the project'].append( (u.pk, u.name)) except KeyError: l_users_dict['not yet in the project'] = [(u.pk, u.name)] self.fields['users'].choices = l_users_dict.items() else: self.fields['users'].queryset = user.users.all() self.fields['refer_client'].widget.attrs['class'] = "selectpicker" self.fields['refer_client'].queryset = user.clients.all() self.fields['refer_project'].widget.attrs['class'] = "selectpicker" self.fields['refer_project'].queryset = user.projects.all() self.fields['duration'].widget.attrs[ 'class'] = "form-control timeduration" self.fields['description'].widget = Textarea(attrs={ 'cols': 80, 'rows': 4 }) class Meta: model = Task fields = [ 'name', 'description', 'refer_client', 'refer_project', 'users', 'tags', 'duration', 'start_date', 'end_date', 'done' ]
def test_durationfield_clean_not_required(self): f = DurationField(required=False) self.assertIsNone(f.clean(''))
def test_durationfield_clean(self): f = DurationField() self.assertEqual(datetime.timedelta(seconds=30), f.clean('30')) self.assertEqual(datetime.timedelta(minutes=15, seconds=30), f.clean('15:30')) self.assertEqual(datetime.timedelta(hours=1, minutes=15, seconds=30), f.clean('1:15:30')) self.assertEqual( datetime.timedelta(days=1, hours=1, minutes=15, seconds=30, milliseconds=300), f.clean('1 1:15:30.3')) self.assertEqual( datetime.timedelta(0, 10800), f.clean(datetime.timedelta(0, 10800)), ) msg = 'This field is required.' with self.assertRaisesMessage(ValidationError, msg): f.clean('') msg = 'Enter a valid duration.' with self.assertRaisesMessage(ValidationError, msg): f.clean('not_a_time')
def test_durationfield_clean(self): f = DurationField() self.assertEqual(datetime.timedelta(seconds=30), f.clean("30")) self.assertEqual(datetime.timedelta(minutes=15, seconds=30), f.clean("15:30")) self.assertEqual(datetime.timedelta(hours=1, minutes=15, seconds=30), f.clean("1:15:30")) self.assertEqual( datetime.timedelta(days=1, hours=1, minutes=15, seconds=30, milliseconds=300), f.clean("1 1:15:30.3"), ) self.assertEqual( datetime.timedelta(0, 10800), f.clean(datetime.timedelta(0, 10800)), ) msg = "This field is required." with self.assertRaisesMessage(ValidationError, msg): f.clean("") msg = "Enter a valid duration." with self.assertRaisesMessage(ValidationError, msg): f.clean("not_a_time") with self.assertRaisesMessage(ValidationError, msg): DurationField().clean("P3(3D")