d6bd78a7bc7db3cbbe3e307217689f6c7376e817
[htsworkflow.git] / experiments / admin.py
1 from __future__ import absolute_import, print_function, unicode_literals
2
3 from itertools import chain
4
5 from django.contrib import admin
6 from django.forms import ModelForm
7 from django.forms.fields import CharField
8 from django.forms.widgets import TextInput, Select
9 from django.utils.encoding import force_text
10 from django.utils.html import escape, conditional_escape
11
12 from .models import (
13     FlowCell,
14     SequencingRun,
15     DataFile,
16     FileType,
17     ClusterStation,
18     Sequencer,
19     Lane
20 )
21
22
23 class DataFileForm(ModelForm):
24     class Meta:
25         model = DataFile
26         fields = ('random_key', 'sequencing_run', 'library',
27                   'file_type', 'relative_pathname')
28
29
30 class DataFileInline(admin.TabularInline):
31     model = DataFile
32     form = DataFileForm
33     raw_id_fields = ('library',)
34     extra = 0
35
36
37 class SequencingRunOptions(admin.ModelAdmin):
38     search_fields = [
39         'flowcell_id',
40         'run_folder',
41         'run_note',
42     ]
43     list_display = [
44         'runfolder_name',
45         'result_dir',
46         'run_start_time',
47     ]
48     fieldsets = (
49         (None, {
50             'fields': (
51                 ('flowcell', 'run_status'),
52                 ('runfolder_name', 'cycle_start', 'cycle_stop'),
53                 ('result_dir',),
54                 ('last_update_time'),
55                 ('image_software', 'image_version'),
56                 ('basecall_software', 'basecall_version'),
57                 ('alignment_software', 'alignment_version'),
58                 ('comment',))
59         }),
60     )
61     inlines = [DataFileInline]
62     # list_filter = ('run_status', 'run_start_time')
63 admin.site.register(SequencingRun, SequencingRunOptions)
64
65
66 class FileTypeAdmin(admin.ModelAdmin):
67     list_display = ('name', 'mimetype', 'regex')
68 admin.site.register(FileType, FileTypeAdmin)
69
70
71 # lane form setup needs to come before Flowcell form config
72 # as flowcell refers to the LaneInline class
73 class LaneForm(ModelForm):
74     comment = CharField(
75         widget=TextInput(attrs={'size': '80'}),
76         required=False)
77
78     class Meta:
79         model = Lane
80         fields = ('flowcell', 'lane_number', 'library',
81                   'pM', 'cluster_estimate',
82                   'status', 'comment')
83
84
85 class LaneInline(admin.StackedInline):
86     """Controls display of Lanes on the Flowcell form.
87     """
88     model = Lane
89     extra = 8
90     form = LaneForm
91     raw_id_fields = ('library',)
92     fieldsets = (
93         (None, {
94             'fields': ('lane_number', 'flowcell',
95                        ('library',),
96                        ('pM', 'cluster_estimate', 'status'),
97                        'comment',)
98         }),
99     )
100
101
102 class LaneOptions(admin.ModelAdmin):
103     """Controls display of Lane browser
104     """
105     search_fields = (
106         '=flowcell__flowcell_id',
107         'library__id',
108         'library__library_name')
109     list_display = ('flowcell', 'lane_number', 'library', 'comment')
110     fieldsets = (
111         (None, {
112             'fields': ('lane_number', 'flowcell',
113                        ('library'),
114                        ('pM', 'cluster_estimate'))
115         }),
116         ('Optional', {
117             'classes': ('collapse', ),
118             'fields': ('comment', )
119         }),
120     )
121 admin.site.register(Lane, LaneOptions)
122
123
124 class FlowCellOptions(admin.ModelAdmin):
125     class Media:
126         css = {'all': ('css/admin_flowcell.css',)}
127     date_hierarchy = "run_date"
128     save_on_top = True
129     search_fields = (
130         'flowcell_id',
131         'sequencer__name',
132         'cluster_station__name',
133         '=lane__library__id',
134         'lane__library__library_name')
135     list_display = ('flowcell_id', 'run_date', 'Lanes')
136     list_filter = ('sequencer', 'cluster_station', 'paired_end')
137     fieldsets = (
138         (None, {
139             'fields': ('run_date',
140                        ('flowcell_id', 'cluster_station', 'sequencer'),
141                        ('read_length', 'control_lane', 'paired_end'),)
142         }),
143         ('Notes:', {'fields': ('notes',), }),
144     )
145     inlines = [
146         LaneInline,
147     ]
148
149     def formfield_for_dbfield(self, db_field, **kwargs):
150         field = super(FlowCellOptions, self).formfield_for_dbfield(db_field,
151                                                                    **kwargs)
152
153         # Override field attributes
154         if db_field.name == 'sequencer':
155             # seems kind of clunky.
156             # the goal is to replace the default select/combo box with one
157             # that can strike out disabled options.
158             attrs = field.widget.widget.attrs
159             field.widget.widget = SequencerSelect(
160                 attrs=attrs,
161                 queryset=field.queryset)
162         elif db_field.name == "notes":
163             field.widget.attrs["rows"] = "3"
164         return field
165 admin.site.register(FlowCell, FlowCellOptions)
166
167
168 class ClusterStationOptions(admin.ModelAdmin):
169     list_display = ('name', 'isdefault',)
170     fieldsets = ((None, {'fields': ('name', 'isdefault')}),)
171 admin.site.register(ClusterStation, ClusterStationOptions)
172
173
174 class SequencerSelect(Select):
175     def __init__(self, queryset=None, *args, **kwargs):
176         super(SequencerSelect, self).__init__(*args, **kwargs)
177         self.queryset = queryset
178
179     def render_options(self, choices, selected_choices):
180         # Normalize to strings.
181         selected_choices = set([force_text(v) for v in selected_choices])
182         output = []
183         for option_value, option_label in chain(self.choices, choices):
184             if isinstance(option_label, (list, tuple)):
185                 output.append(u'<optgroup label="%s">' %
186                               escape(force_text(option_value)))
187                 for option in option_label:
188                     output.append(
189                         self.render_option(selected_choices,
190                                            *option))
191                 output.append(u'</optgroup>')
192             else:
193                 output.append(
194                     self.render_option(selected_choices,
195                                        option_value,
196                                        option_label))
197         return u'\n'.join(output)
198
199     def render_option(self, selected_choices, option_value, option_label):
200         disabled_sequencers = [str(s.id) for s in
201                                self.queryset.filter(active=False)]
202         option_value = str(option_value)
203         selected_html = (option_value in selected_choices) and \
204                         u' selected="selected"' or ''
205         cssclass = "strikeout" if option_value in disabled_sequencers else ''
206         return u'<option class="%s" value="%s"%s>%s</option>' % (
207             cssclass, escape(option_value), selected_html,
208             conditional_escape(force_text(option_label)))
209
210
211 class SequencerOptions(admin.ModelAdmin):
212     list_display = ('name', 'active', 'isdefault', 'instrument_name', 'model')
213     fieldsets = ((None,
214                   {'fields': (
215                       'name',
216                       ('active', 'isdefault'),
217                       'instrument_name',
218                       'serial_number',
219                       'model', 'comment')}), )
220
221 admin.site.register(Sequencer, SequencerOptions)