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