def test_get_worms_for_screen_type(self): n2, dnc1, glp1, emb8 = self.get_worms() enhs = WormStrain.get_worms_for_screen_type('ENH') sups = WormStrain.get_worms_for_screen_type('SUP') for worms in (enhs, sups): self.assertNotIn(n2, worms) self.assertIn(emb8, worms) self.assertIn(dnc1, enhs) self.assertNotIn(dnc1, sups) self.assertNotIn(glp1, enhs) self.assertIn(glp1, sups)
def get_n2_control_filters(self): """ Get the N2 + RNAi controls for this experiment. It does this by manually setting the strain as N2 and then applying all the other parameters which are shared by the controls. To get the actual control experiments from the returned filters, simply do Experiment.objects.filter(**filters). Returning the filters is more flexible for customization, or for inserting into a URL without performing the query. N2 controls for this experiment are restricted to those from the same date, *closest* temperature, same RNAi clone. """ filters = { 'is_junk': False, 'plate__date': self.date(), 'plate__temperature': self.temperature(), 'library_stock': self.library_stock, 'worm_strain': WormStrain.get_n2().pk, } filters['plate__temperature'] = Experiment.get_closest_temperature( self.temperature(), filters) return filters
def get_n2_l4440_control_filters(self): """ Get the filters for the L4440 controls for this experiment. To get the actual control experiments from the returned filters, simply do Experiment.objects.filter(**filters). Returning the filters is more flexible for customization, or for inserting into a URL without performing the query. L4440 controls for this experiment are restricted to those from the same date, same temperature, same worm. If this experiment is itself an L4440 clone, the function works the same way, returning all L4440 experiments from the same date, same worm, same temperature. """ filters = { 'is_junk': False, 'plate__date': self.date(), 'plate__temperature': self.temperature(), 'worm_strain': WormStrain.get_n2().pk, 'library_stock__intended_clone': Clone.get_l4440(), } return filters
def clean_mutant_query_and_screen_type(form, cleaned_data): """ Helper to derive worm strain and temperature from cleaned_data. If cleaned_data['mutant_query'] and cleaned_data['screen_type'] are defined, cleaned_data['worm'] and cleaned_data['temperature'] will be populated. Returns the modified cleaned_data. """ mutant_query = cleaned_data.get('mutant_query') secondary_mutant_query = cleaned_data.get('secondary_mutant_query') screen_type = cleaned_data.get('screen_type') if mutant_query and screen_type and secondary_mutant_query: worm_and_temp = WormStrain.get_worm_and_temperature_from_search_term( mutant_query, screen_type) worm_and_temp2 = WormStrain.get_worm_and_temperature_from_search_term( secondary_mutant_query, screen_type) print worm_and_temp2 if worm_and_temp and worm_and_temp2: cleaned_data['worm'] = worm_and_temp[0] cleaned_data['worm2'] = worm_and_temp2[0] cleaned_data['temperature'] = worm_and_temp[1] else: form.add_error('mutant_query', 'No mutant match') elif mutant_query and screen_type: worm_and_temp = WormStrain.get_worm_and_temperature_from_search_term( mutant_query, screen_type) if worm_and_temp: cleaned_data['worm'] = worm_and_temp[0] cleaned_data['worm2'] = secondary_mutant_query cleaned_data['temperature'] = worm_and_temp[1] else: form.add_error('mutant_query', 'No mutant match') return cleaned_data
def rnai_knockdown(request, clones, temperature=None): """ Render the page showing knockdown by RNAi only. context['data'] is returned in format: {clone: { library_stock: [experiments] }} """ data = OrderedDict() n2 = WormStrain.get_n2() clones = Clone.objects.filter(pk__in=clones.split(',')) for clone in clones: filters = { 'is_junk': False, 'worm_strain': n2.pk, 'library_stock__intended_clone': clone, } if temperature: filters['plate__temperature'] = temperature # Do not join manual scores, since N2 not manually scored experiments = (Experiment.objects.filter(**filters).select_related( 'library_stock', 'plate').prefetch_related('devstarscore_set').order_by( '-library_stock__plate__screen_stage', 'library_stock', '-plate__date', 'id')) data_by_well = OrderedDict() for experiment in experiments: library_stock = experiment.library_stock if library_stock not in data_by_well: data_by_well[library_stock] = [] data_by_well[library_stock].append(experiment) if data_by_well: data[clone] = data_by_well context = { 'n2': n2, 'clones': clones, 'temperature': temperature, 'data': data, } return render(request, 'rnai_knockdown.html', context)
def rnai_knockdown(request, clones, temperature=None): """ Render the page showing knockdown by RNAi only. context['data'] is returned in format: {clone: { library_stock: [experiments] }} """ data = OrderedDict() n2 = WormStrain.get_n2() clones = Clone.objects.filter(pk__in=clones.split(',')) for clone in clones: filters = { 'is_junk': False, 'worm_strain': n2.pk, 'library_stock__intended_clone': clone, } if temperature: filters['plate__temperature'] = temperature # Do not join manual scores, since N2 not manually scored experiments = (Experiment.objects.filter(**filters) .select_related('library_stock', 'plate') .prefetch_related('devstarscore_set') .order_by('-library_stock__plate__screen_stage', 'library_stock', '-plate__date', 'id')) data_by_well = OrderedDict() for experiment in experiments: library_stock = experiment.library_stock if library_stock not in data_by_well: data_by_well[library_stock] = [] data_by_well[library_stock].append(experiment) if data_by_well: data[clone] = data_by_well context = { 'n2': n2, 'clones': clones, 'temperature': temperature, 'data': data, } return render(request, 'rnai_knockdown.html', context)
def get_positives_any_worm(screen_type, screen_stage, criteria, **kwargs): """ Get the set of library stocks that are positive for ANY worm. A screen is defined by both screen_type ('ENH' or 'SUP') and screen_stage (1 for primary, 2 for secondary). """ worms = WormStrain.get_worms_for_screen_type(screen_type) all_positives = set() for worm in worms: positives = worm.get_positives(screen_type, screen_stage, criteria, **kwargs) all_positives = all_positives.union(positives) return all_positives
def _limit_to_screen_type(experiments, screen_type): """ Post-process experiments QuerySet such that each experiment was done at its worm's SUP or ENH temperature. Since N2 does not have a SUP or ENH temperature, N2 will not be in this result. Question: Why not just get the SUP/ENH temperature for these experiments' worm, and then using `.filter()` with that temperature? Answer: That is what I do on queries limited to a single worm strain, e.g. for most of the public-facing pages. But these experiment filtering forms are meant to be flexible (basically a gateway into the database for GI team use only), flexible enough to potentially include multiple strains with different SUP/ENH temperatures (e.g. maybe Noah wants to see all experiments from one date). Question: Why not just join between ExperimentPlate.temperature and WormStrain.permissive_temperature / .restrictive_temperature? This would involve joining WormStrain on a second field. While easy with raw SQL, this is not easy with Django, requiring either 1) soon-to- be-deprecated `empty()`, 2) overriding low level query processing in ways that are subject to syntax changes, or 3) using `raw()` to write raw SQL. While I was tempted to do 3), since these filtering forms are meant to be generic and applicable (able to take dozens of possible keys to filter on), this one case doesn't warrant losing the readability and protections against SQL injection attacks that Django QuerySets provide. """ # Create a dictionary to_temperature = WormStrain.get_worm_to_temperature_dictionary(screen_type) filtered = [] for experiment in experiments.prefetch_related("worm_strain", "plate"): temperature = experiment.plate.temperature if temperature == to_temperature[experiment.worm_strain]: filtered.append(experiment) return filtered
def clean_mutant_query_and_screen_type(form, cleaned_data): """ Helper to derive worm strain and temperature from cleaned_data. If cleaned_data['mutant_query'] and cleaned_data['screen_type'] are defined, cleaned_data['worm'] and cleaned_data['temperature'] will be populated. Returns the modified cleaned_data. """ mutant_query = cleaned_data.get('mutant_query') screen_type = cleaned_data.get('screen_type') if mutant_query and screen_type: worm_and_temp = WormStrain.get_worm_and_temperature_from_search_term( mutant_query, screen_type) if worm_and_temp: cleaned_data['worm'] = worm_and_temp[0] cleaned_data['temperature'] = worm_and_temp[1] else: form.add_error('mutant_query', 'No mutant match') return cleaned_data
def get_n2_control_filters(self): """ Get the N2 + RNAi controls for this experiment. To get the actual control experiments from the returned filters, simply do Experiment.objects.filter(**filters). Returning the filters is more flexible for customization, or for inserting into a URL without performing the query. N2 controls for this experiment are restricted to those from the same date, *closest* temperature, same RNAi clone. """ filters = { 'is_junk': False, 'plate__date': self.date(), 'plate__temperature': self.temperature(), 'library_stock': self.library_stock, 'worm_strain': WormStrain.get_n2().pk, } filters['plate__temperature'] = Experiment.get_closest_temperature( self.temperature(), filters) return filters
def double_knockdown(request, mutant, clones, temperature): """ Render the page showing knockdown by both mutation and RNAi. context['data'] is returned in format: {clone: { library_stock: { date: { 'mutant_rnai': { 'experiments': [experiments], 'link_to_all': url }, 'n2_rnai': { 'experiments': [experiments], 'link_to_all': url }, 'mutant_l4440': { 'experiments': [experiments], 'link_to_all': url }, 'n2_l4440': { 'experiments': [experiments], 'link_to_all': url }, } } }} """ data = OrderedDict() n2 = WormStrain.get_n2() l4440 = Clone.get_l4440() mutant = get_object_or_404(WormStrain, pk=mutant) clones = Clone.objects.filter(pk__in=clones.split(',')) for clone in clones: data_per_clone = OrderedDict() library_stocks = (LibraryStock.objects.filter( intended_clone=clone).order_by('-plate__screen_stage', 'id')) for library_stock in library_stocks: data_per_well = OrderedDict() dates = Experiment.get_distinct_dates({ 'is_junk': False, 'worm_strain': mutant, 'library_stock': library_stock, 'plate__temperature': temperature, }) for date in dates: # Add double knockdowns filters = { 'is_junk': False, 'plate__date': date, 'worm_strain': mutant.pk, 'library_stock': library_stock, 'plate__temperature': temperature, } mutant_rnai = _create_inner_dictionary(filters, join_manual=True) # Add mutant + L4440 controls filters = { 'is_junk': False, 'plate__date': date, 'worm_strain': mutant.pk, 'library_stock__intended_clone': l4440, 'plate__temperature': temperature, } mutant_l4440 = _create_inner_dictionary(filters) # Add N2 + RNAi controls filters = { 'is_junk': False, 'plate__date': date, 'worm_strain': n2.pk, 'library_stock': library_stock, } t = Experiment.get_closest_temperature(temperature, filters) filters['plate__temperature'] = t n2_rnai = _create_inner_dictionary(filters) # Add N2 + L4440 controls filters = { 'is_junk': False, 'plate__date': date, 'worm_strain': n2.pk, 'library_stock__intended_clone': l4440, } t = Experiment.get_closest_temperature(temperature, filters) filters['plate__temperature'] = t n2_l4440 = _create_inner_dictionary(filters) data_per_well[date] = { 'mutant_rnai': mutant_rnai, 'mutant_l4440': mutant_l4440, 'n2_rnai': n2_rnai, 'n2_l4440': n2_l4440, } if data_per_well: data_per_clone[library_stock] = data_per_well data[clone] = data_per_clone context = { 'mutant': mutant, 'clones': clones, 'temperature': temperature, 'data': data, } return render(request, 'double_knockdown.html', context)
def double_knockdown(request, mutant, clones, temperature): """ Render the page showing knockdown by both mutation and RNAi. context['data'] is returned in format: {clone: { library_stock: { date: { 'mutant_rnai': { 'experiments': [experiments], 'link_to_all': url }, 'n2_rnai': { 'experiments': [experiments], 'link_to_all': url }, 'mutant_l4440': { 'experiments': [experiments], 'link_to_all': url }, 'n2_l4440': { 'experiments': [experiments], 'link_to_all': url }, } } }} """ data = OrderedDict() n2 = WormStrain.get_n2() l4440 = Clone.get_l4440() mutant = get_object_or_404(WormStrain, pk=mutant) clones = Clone.objects.filter(pk__in=clones.split(',')) for clone in clones: data_per_clone = OrderedDict() library_stocks = (LibraryStock.objects.filter(intended_clone=clone) .order_by('-plate__screen_stage', 'id')) for library_stock in library_stocks: data_per_well = OrderedDict() dates = Experiment.get_distinct_dates({ 'is_junk': False, 'worm_strain': mutant, 'library_stock': library_stock, 'plate__temperature': temperature, }) for date in dates: # Add double knockdowns filters = { 'is_junk': False, 'plate__date': date, 'worm_strain': mutant.pk, 'library_stock': library_stock, 'plate__temperature': temperature, } mutant_rnai = _create_inner_dictionary( filters, join_manual=True) # Add mutant + L4440 controls filters = { 'is_junk': False, 'plate__date': date, 'worm_strain': mutant.pk, 'library_stock__intended_clone': l4440, 'plate__temperature': temperature, } mutant_l4440 = _create_inner_dictionary(filters) # Add N2 + RNAi controls filters = { 'is_junk': False, 'plate__date': date, 'worm_strain': n2.pk, 'library_stock': library_stock, } t = Experiment.get_closest_temperature(temperature, filters) filters['plate__temperature'] = t n2_rnai = _create_inner_dictionary(filters) # Add N2 + L4440 controls filters = { 'is_junk': False, 'plate__date': date, 'worm_strain': n2.pk, 'library_stock__intended_clone': l4440, } t = Experiment.get_closest_temperature(temperature, filters) filters['plate__temperature'] = t n2_l4440 = _create_inner_dictionary(filters) data_per_well[date] = { 'mutant_rnai': mutant_rnai, 'mutant_l4440': mutant_l4440, 'n2_rnai': n2_rnai, 'n2_l4440': n2_l4440, } if data_per_well: data_per_clone[library_stock] = data_per_well data[clone] = data_per_clone context = { 'mutant': mutant, 'clones': clones, 'temperature': temperature, 'data': data, } return render(request, 'double_knockdown.html', context)
def handle(self, **options): summary_mode = options['summary'] candidates_by_worm = {} candidates_by_clone = {} worms = WormStrain.get_worms_for_screen_type('ENH') for worm in worms: candidates_by_worm[worm] = [] singles = worm.get_stocks_tested_by_number_of_replicates( 'ENH', 1, 1) positives = worm.get_positives('ENH', 1, passes_enh_primary, singles=singles) for library_stock in positives: candidates_by_worm[worm].append(library_stock) if library_stock not in candidates_by_clone: candidates_by_clone[library_stock] = [] candidates_by_clone[library_stock].append(worm) if summary_mode: self.stdout.write('Total clones to cherrypick: {}'.format( len(candidates_by_clone))) self.stdout.write('\n\nBefore accounting for universals:') _print_candidates_by_worm(candidates_by_worm) # Move certain clones from individual worm lists to universal candidates_by_worm['universal'] = [] for well in candidates_by_clone: worms = (candidates_by_clone[well]) if len(worms) >= UNIVERSAL_THRESHOLD: candidates_by_worm['universal'].append(well) for worm in worms: candidates_by_worm[worm].remove(well) if summary_mode: self.stdout.write('\n\nAfter accounting for universals:') _print_candidates_by_worm(candidates_by_worm) return # Create official cherrypick list, with random empty wells cherrypick_list = [] already_used_empties = set() for worm, candidates in candidates_by_worm.iteritems(): label = worm.allele if hasattr(worm, 'allele') else worm if label == 'universal': empties_per_plate = 1 empties_limit = 9 elif label == 'it57': empties_per_plate = 0 empties_limit = None else: empties_per_plate = 2 empties_limit = None assigned = assign_to_plates( sorted(candidates), vertical=True, empties_per_plate=empties_per_plate, empties_limit=empties_limit, already_used_empties=already_used_empties) rows = get_plate_assignment_rows(assigned) for row in rows: if row[2]: source_plate = row[2].plate source_well = row[2].well else: # Empty well source_plate = None source_well = None destination_plate = label + '-E' + str(row[0] + 1) destination_well = row[1] cherrypick_list.append((source_plate, source_well, destination_plate, destination_well)) # Sort by (destination_plate, destination_well) cherrypick_list.sort(key=lambda x: (x[2].split('-')[ 0], int(x[2].split('E')[1]), int(x[3][1:]), x[3][0])) # Print the list self.stdout.write('source_plate,source_well,' 'destination_plate,destination_well') for row in cherrypick_list: self.stdout.write(','.join([str(x) for x in row])) # Quick fix for empty_wells check up to this point not accounting # for not-full plates potentially having the same plate pattern, # despite the "chosen" empty wells differing. # If the printed list says "TRASH THIS" at the bottom, try again! e = {} for row in cherrypick_list: if row[0] is None: if row[2] not in e: e[row[2]] = set() e[row[2]].add(row[3]) seen = set() for plate, wells in e.iteritems(): wells = tuple(sorted(wells)) if is_symmetric(wells): self.stdout.write('TRASH THIS AND TRY AGAIN. ' '{}:{} pattern is symmetric!'.format( plate, wells)) if wells in seen: self.stdout.write('TRASH THIS AND TRY AGAIN. ' '{}:{} pattern redundant!'.format( plate, wells)) seen.add(wells)
def test_get_n2(self): n2 = WormStrain.get_n2() self.assertEquals(n2.id, 'N2') self.assertIsNone(n2.permissive_temperature) self.assertIsNone(n2.restrictive_temperature)