def getSampleWidgets( extra={} ): """widgets shared between different types of Sample forms.""" r = { 'container' : sforms.AutoComboboxSelectWidget(lookup_class=L.SampleContainerLookup, allow_new=False), 'displayId' : forms.TextInput(attrs={'size':5}), 'preparedBy' : sforms.AutoComboboxSelectWidget(lookup_class=L.UserLookup, allow_new=False, attrs={'size':15}), 'experimentNr' : forms.TextInput(attrs={'size':15}), 'concentration' : forms.TextInput(attrs={'size':5}), 'concentrationUnit' : sforms.AutoComboboxSelectWidget(lookup_class=L.ConcentrationUnitLookup, allow_new=False,attrs={'size':5}), 'amount' : forms.TextInput(attrs={'size':5}), 'amountUnit' : sforms.AutoComboboxSelectWidget(lookup_class=L.VolumeAmountUnitLookup, allow_new=False,attrs={'size':5}), 'aliquotNr' : forms.TextInput(attrs={'size':2}), 'description': forms.Textarea(attrs={'cols': 100, 'rows': 5, 'style':'font-family:monospace'}) } r.update( extra ) return r
class Meta(object): model = Direccion widgets = { 'estado': MXStateSelect(), 'municipio': selectable.AutoComboboxSelectWidget( lookup_class=MunicipioLookup, ), 'colonia': selectable.AutoComboboxSelectWidget(lookup_class=ColoniaLookup, ), } exclude = ('', )
class Meta: model = M.DnaComponent widgets = getComponentWidgets( extra={ 'name': forms.TextInput( attrs={ 'size': 25, 'title': 'Erase in order to re-activate automatic name composition from insert and vector.' }), 'sequence': forms.Textarea(attrs={ 'cols': 100, 'rows': 4, 'style': 'font-family:monospace' }), 'insert': sforms.AutoComboboxSelectWidget( lookup_class=L.InsertLookup, allow_new=False, attrs={ 'size': 32, 'title': 'Select a DNA construct (must be classified as "Fragment").' }), 'vectorBackbone': sforms.AutoComboboxSelectWidget( lookup_class=L.VectorLookup, allow_new=False, attrs={ 'title': 'Select a DNA construct (must be classified as "Vector Backbone").' }), 'markers': L.FixedSelectMultipleWidget( lookup_class=L.MarkerLookup, attrs={ 'title': 'Select one or more DNA constructs (must be classified as "Marker").' }), 'translatesTo': sforms.AutoComboboxSelectWidget( lookup_class=L.ProteinLookup, allow_new=False, attrs={'title': 'Select a Protein construct.'}), })
class Meta: model = M.OligoSample widgets = getSampleWidgets( \ {'oligo': sforms.AutoComboboxSelectWidget(lookup_class=L.OligoLookup, allow_new=False, attrs={'size':35}), })
class Meta: model = M.ProteinSample widgets = getSampleWidgets( \ {'protein': sforms.AutoComboboxSelectWidget(lookup_class=L.ProteinLookup, allow_new=False, attrs={'size':35}), })
class Meta: model = M.ChemicalSample widgets = getSampleWidgets( \ {'chemical': sforms.AutoComboboxSelectWidget(lookup_class=L.ChemicalLookup, allow_new=False, attrs={'size':35}), })
class Meta: model = M.CellComponent widgets = getComponentWidgets( extra={ 'name': forms.TextInput( attrs={ 'size': 30, 'title': 'Erase in order to re-activate automatic name composition from plasmid and strain.' }), 'plasmid': sforms.AutoComboboxSelectWidget( lookup_class=L.PlasmidLookup, allow_new=False, attrs={ 'size': 35, 'title': 'Select a DNA construct (must be classified as "Plasmid").' }), 'markers': L.FixedSelectMultipleWidget( lookup_class=L.MarkerLookup, attrs={ 'title': 'Select one or more DNA constructs (must be classified as "Marker").' }) })
class Meta: model = M.DnaSample widgets = getSampleWidgets( \ {'dna': sforms.AutoComboboxSelectWidget(lookup_class=L.SampleDnaLookup, allow_new=False, attrs={'size':35}), })
class Meta: model = M.Container widgets = { ## customize widget dimensions and include dynamic select widgets 'displayId' : forms.TextInput(attrs={'size':10}), 'rack' : sforms.AutoComboboxSelectWidget( lookup_class=L.ContainerRackLookup, allow_new=False), 'name' : forms.TextInput(attrs={'size':20}), }
class Meta: model = Entry exclude = ('user', 'pause_time', 'site', 'hours', 'status', 'entry_group') widgets = { 'activity': selectable.AutoComboboxSelectWidget(lookup_class=ActivityLookup), }
class Meta: model = M.Sequencing widgets = { 'sample': sforms.AutoComboboxSelectWidget(lookup_class=L.DnaSampleLookup, allow_new=False, attrs={'size': 15}), 'orderedBy': sforms.AutoComboboxSelectWidget(lookup_class=L.UserLookup, allow_new=False, attrs={'size': 15}), 'comments': forms.Textarea(attrs={ 'rows': 5, 'cols': 80 }) }
class Meta: model = Entry fields = ('active_comment', 'location', 'project', 'user_story', 'task', 'activity', 'start_time', 'comments') widgets = { 'activity': selectable.AutoComboboxSelectWidget(lookup_class=ActivityLookup), }
class AccountForm(EditProfileForm): native_region = selectable.AutoCompleteSelectField( lookup_class=RegionLookup, required=False, widget=selectable.AutoComboboxSelectWidget(lookup_class=RegionLookup)) class Meta: model = Account fields = [ 'mugshot', 'date_of_birth', 'native_region', 'tax_origin_confirmed', 'not_from_canada', 'fb_pages' ] widgets = {'fb_pages': AddFacebookPagesWidget}
class Meta: model = M.SequencingRun widgets = { 'primer': sforms.AutoComboboxSelectWidget( lookup_class=L.SequencingOligoLookup, allow_new=False, attrs={'size': 15}), 'description': forms.Textarea(attrs={ 'rows': 2, 'cols': 40 }) }
class LotesAdminForm(forms.ModelForm): class Meta(object): model = Lotes oproduccion = forms.CharField( label='Orden Produccion', widget=selectable.AutoComboboxSelectWidget(OProduccionLookup), required=True) fecha = forms.DateField() lote = forms.CharField() analiticas = forms.ComboField() procesos = forms.ComboField() templote = forms.CharField() carorganolep = forms.CharField() observaciones = forms.CharField()
class InstructorsForm(forms.Form): '''Represent instructor matching form.''' latitude = forms.FloatField(label='Latitude', min_value=-90.0, max_value=90.0, required=False) longitude = forms.FloatField(label='Longitude', min_value=-180.0, max_value=180.0, required=False) airport = selectable.AutoCompleteSelectField( lookup_class=lookups.AirportLookup, label='Airport', required=False, widget=selectable.AutoComboboxSelectWidget( lookup_class=lookups.AirportLookup, ), ) country = forms.MultipleChoiceField(choices=[]) lessons = forms.ModelMultipleChoiceField(queryset=Lesson.objects.all(), widget=CheckboxSelectMultiple(), required=False) INSTRUCTOR_BADGE_CHOICES = ( ('swc-instructor', 'Software Carpentry Instructor'), ('dc-instructor', 'Data Carpentry Instructor'), ) instructor_badges = forms.MultipleChoiceField( choices=INSTRUCTOR_BADGE_CHOICES, widget=CheckboxSelectMultiple(), required=False, ) GENDER_CHOICES = ((None, '---------'), ) + Person.GENDER_CHOICES gender = forms.ChoiceField(choices=GENDER_CHOICES, required=False) def __init__(self, *args, **kwargs): '''Build form layout dynamically.''' super(InstructorsForm, self).__init__(*args, **kwargs) # dynamically build choices for country field only = Airport.objects.distinct().values_list('country', flat=True) only = [c for c in only if c] countries = Countries() countries.only = only choices = list(countries) self.fields['country'] = forms.MultipleChoiceField(choices=choices, required=False) self.helper = FormHelper(self) self.helper.form_class = 'form-inline' self.helper.form_method = 'get' self.helper.layout = Layout( Div( Div('latitude', 'longitude', css_class='panel-body'), css_class='panel panel-default ', ), HTML('<p>OR</p>'), Div( Div('airport', css_class='panel-body'), css_class='panel panel-default ', ), HTML('<p>OR</p>'), Div( Div('country', css_class='panel-body'), css_class='panel panel-default ', ), 'gender', 'lessons', 'instructor_badges', FormActions(Submit('submit', 'Submit'), ), ) def clean(self): cleaned_data = super(InstructorsForm, self).clean() airport = cleaned_data.get('airport') lat = cleaned_data.get('latitude') long = cleaned_data.get('longitude') country = cleaned_data.get('country') sum = bool(airport) + bool(lat and long) + bool(country) # user can specify only one: either airport, or lat&long, or country if sum != 1: raise forms.ValidationError('Must specify an airport, or latitude' ' and longitude, or a country.') return cleaned_data
class Meta: widgets = {'sourceSample': sforms.AutoComboboxSelectWidget(lookup_class=L.SampleLookup, allow_new=False, attrs={'size':20}) }
class TracesUploadForm(UploadFormBase): """Form for attaching multiple sequencing trace files to selected DNA samples""" MATCHCHOICES = (('s', 'sample ID (e.g. A01_comment.ab1)'), ('s.dna', 'construct ID (e.g. rg0011_comment.ab1)'), ('s.container:s', 'container + sample ID (e.g. Dna20_A01_comment.ab1)'), ('s:s.dna', 'sample ID + construct ID (e.g. A01_rg0011_comment.ab1)')) samples = forms.ModelMultipleChoiceField( M.DnaSample.objects.all(), cache_choices=False, required=True, widget=sforms.AutoComboboxSelectMultipleWidget( lookup_class=L.DnaSampleLookup), label='Samples', initial=None, help_text= 'Start typing container, sample or construct ID to restrict the choice.' ) matchBy = forms.ChoiceField( label='match by', choices=MATCHCHOICES, initial='s', widget=forms.RadioSelect, required=True, help_text="""Select how trace file names are matched to samples. File names need to contain certain characters that separate the different IDs from each other and from the rest of the name. Allowed separating characters are: '_'(underscore), ' '(space), ':'(colon), ';'(semicolon), '-'(minus). IDs are converted to lower case and leading zeros are removed to make the matching more robust. For example, let's assume you have a sample with ID 'A01' which contains the DNA construct with ID 'sb0001'. If you select sample ID + construct ID matching, the following file names are all correct and will match the trace to this sample: A1_sb0001_mysequencing.ab1 or a1-sb1-mysequencing.ab1 or A01:sb001_mysequencing.ab1 """) matchPrimer = forms.BooleanField( required=True, label='extract primer ID', initial=True, help_text="""Try to find a sequencing primer ID within the file name. The trace file name may contain the ID of a sequencing primer. This ID has to be an exact match (including capitalization/case and leading zeros) with the primer ID. The primer name does <em>not</em> work. The import will proceed even if there is no match, in which case the primer field is left empty for this trace. """) files = MultiFileField( label='Trace files:', min_num=2, extensions=['ab', 'abi', 'ab1', 'scf', 'phd'], help_text= 'hold <CTRL> to select multiple files. Expected formats are: *.ab, *.abi, *.ab1,*.scf, *.phd' ) evaluation = forms.ChoiceField( label='evaluation', choices=M.Sequencing.EVALUATIONS, initial='none', required=True, help_text='pre-set sequencing verdict with respect to target') orderedAt = forms.DateField(initial=datetime.now().date, label="ordered", widget=AdminDateWidget) orderedBy = forms.ModelChoiceField( User.objects.all(), required=True, label='By', widget=sforms.AutoComboboxSelectWidget(lookup_class=L.UserLookup, allow_new=False, attrs={'size': 15}), help_text= 'User responsible for this sequencing\nStart typing user name to restrict choices.' ) comments = forms.CharField( label='Comments', required=False, widget=forms.Textarea(attrs={ 'rows': 4, 'cols': 80 }), help_text= 'This same comment will be put into every new sequencing record') def __init__(self, *arg, **kwarg): super(TracesUploadForm, self).__init__(*arg, **kwarg) ## Note: the 'self.fields['orderedBy'].initial =' syntax doesn't work with selectable fields self.initial['orderedBy'] = str(self.request.user.id) def _matchitems(self, fields, sample): """ Extract displayIds from sample or sample sub-fields specified in fields. Normalize each ID to lower case and remove any leading zeros @return [str] - list of one or two IDs """ s = sample ## fields are supposed to be 's' or 's.container' etc. r = [eval(f + '.displayId') for f in fields] r = [self.normalize(s) for s in r] return tuple(r) def clean_samples(self): """ Ensure unique sample IDs needed for file name matching Convert sample list into a dict indexed by the ID or IDs used for matching. """ data = self.cleaned_data['samples'] fields = self.data['matchBy'].split(':') sdic = {self._matchitems(fields, s): s for s in data} ## verify that all sample ids are unique with the current match if len(sdic) < len(data): raise forms.ValidationError(\ 'Sorry, there are samples with identical IDs in the selected set. '+\ 'Select fewer samples or change the file name matching below.', code='invalid') return sdic def _findsample(self, fname, sdic): n_fields = len( sdic.keys()[0]) ## how many fragments are supposed to match? sep = self.ID_SEPARATORS frags = [self.normalize(x) for x in re.split(sep, fname)] searchfrags = tuple( frags[:n_fields]) ## should be first 1 or 2 fragments if searchfrags in sdic: return sdic[searchfrags] self.add_error('files',\ 'Cannot match %s to any of the samples.' % fname +\ 'No sample can be identified by %s.' % ' + '.join(searchfrags) ) return None def findprimer(self, fname, primers=[]): """try identifying a sequencing primer ID in the filename""" for p in primers: if p.displayId in fname: return p return None def mapTracesToSamples(self, files): """map given InMemoryFileUpload files to samples by name""" sdic = self.cleaned_data['samples'] r = {s: [] for s in sdic.values()} for f in files: s = self._findsample(f.name, sdic) if s: r[s] += [f] if [] in r.values(): missing = [str(s) for s in r.keys() if r[s] == []] self.add_error('files', 'Could not find traces for the following samples: '+\ ', '.join(missing)) return r def createSeq(self, sample, traces, **kwargs): """Create and save new sequencing and sequencingRun instances""" for key in ['orderedBy', 'orderedAt', 'evaluation', 'comments']: kwargs[key] = self.cleaned_data[key] kwargs['registeredBy'] = self.request.user kwargs['registeredAt'] = datetime.now() kwargs['comments'] += '\n(Created through trace file upload)' r = M.Sequencing(sample=sample, **kwargs) r.save() seqprimers = M.OligoComponent.objects.filter( componentType=T.ocSequencing) for f in traces: if self.cleaned_data['matchPrimer']: primer = self.findprimer(f.name, seqprimers) run = M.SequencingRun(parent=r, f=f, description='multiple file upload', primer=primer) run.save()