e22f8ec258a07a6896898c3600072f776cd4ac43
[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 @admin.register(SequencingRun)
38 class SequencingRunOptions(admin.ModelAdmin):
39     search_fields = [
40         'flowcell_id',
41         'run_folder',
42         'run_note',
43     ]
44     list_display = [
45         'runfolder_name',
46         'result_dir',
47         'run_start_time',
48     ]
49     fieldsets = (
50         (None, {
51             'fields': (
52                 ('flowcell', 'run_status'),
53                 ('runfolder_name', 'cycle_start', 'cycle_stop'),
54                 ('result_dir',),
55                 ('last_update_time'),
56                 ('image_software', 'image_version'),
57                 ('basecall_software', 'basecall_version'),
58                 ('alignment_software', 'alignment_version'),
59                 ('comment',))
60         }),
61     )
62     inlines = [DataFileInline]
63     # list_filter = ('run_status', 'run_start_time')
64
65
66 @admin.register(FileType)
67 class FileTypeAdmin(admin.ModelAdmin):
68     list_display = ('name', 'mimetype', 'regex')
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 @admin.register(Lane)
103 class LaneOptions(admin.ModelAdmin):
104     """Controls display of Lane browser
105     """
106     search_fields = (
107         '=flowcell__flowcell_id',
108         'library__id',
109         'library__library_name')
110     list_display = ('flowcell', 'lane_number', 'library', 'comment')
111     fieldsets = (
112         (None, {
113             'fields': ('lane_number', 'flowcell',
114                        ('library'),
115                        ('pM', 'cluster_estimate'))
116         }),
117         ('Optional', {
118             'classes': ('collapse', ),
119             'fields': ('comment', )
120         }),
121     )
122
123
124 @admin.register(FlowCell)
125 class FlowCellOptions(admin.ModelAdmin):
126     class Media:
127         css = {'all': ('css/admin_flowcell.css',)}
128     date_hierarchy = "run_date"
129     save_on_top = True
130     search_fields = (
131         'flowcell_id',
132         'sequencer__name',
133         'cluster_station__name',
134         '=lane__library__id',
135         'lane__library__library_name')
136     list_display = ('flowcell_id', 'run_date', 'Lanes')
137     list_filter = ('sequencer', 'cluster_station', 'paired_end')
138     fieldsets = (
139         (None, {
140             'fields': ('run_date',
141                        ('flowcell_id', 'cluster_station', 'sequencer'),
142                        ('read_length', 'control_lane', 'paired_end'),)
143         }),
144         ('Notes:', {'fields': ('notes',), }),
145     )
146     inlines = [
147         LaneInline,
148     ]
149
150     def formfield_for_dbfield(self, db_field, **kwargs):
151         field = super(FlowCellOptions, self).formfield_for_dbfield(db_field,
152                                                                    **kwargs)
153
154         # Override field attributes
155         if db_field.name == 'sequencer':
156             # seems kind of clunky.
157             # the goal is to replace the default select/combo box with one
158             # that can strike out disabled options.
159             attrs = field.widget.widget.attrs
160             field.widget.widget = SequencerSelect(
161                 attrs=attrs,
162                 queryset=field.queryset)
163         elif db_field.name == "notes":
164             field.widget.attrs["rows"] = "3"
165         return field
166
167
168 @admin.register(ClusterStation)
169 class ClusterStationOptions(admin.ModelAdmin):
170     list_display = ('name', 'isdefault',)
171     fieldsets = ((None, {'fields': ('name', 'isdefault')}),)
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 @admin.register(Sequencer)
212 class SequencerOptions(admin.ModelAdmin):
213     list_display = ('name', 'active', 'isdefault', 'instrument_name', 'model')
214     fieldsets = ((None,
215                   {'fields': (
216                       'name',
217                       ('active', 'isdefault'),
218                       'instrument_name',
219                       'serial_number',
220                       'model', 'comment')}), )