diff --git a/qiita_db/analysis.py b/qiita_db/analysis.py index 1f76610b6..a0afc9022 100644 --- a/qiita_db/analysis.py +++ b/qiita_db/analysis.py @@ -18,6 +18,7 @@ from __future__ import division from collections import defaultdict +from qiita_core.exceptions import IncompetentQiitaDeveloperError from .sql_connection import SQLConnectionHandler from .base import QiitaStatusObject from .exceptions import QiitaDBNotImplementedError, QiitaDBStatusError @@ -34,6 +35,7 @@ class Analysis(QiitaStatusObject): name description samples + data_types biom_tables shared_with jobs @@ -187,6 +189,23 @@ def samples(self): ret_samples[pid].append(sample) return ret_samples + @property + def data_types(self): + """Returns all data types used in the analysis + + Returns + ------- + list of str + Data types in the analysis + """ + sql = ("SELECT DISTINCT data_type from qiita.data_type d JOIN " + "qiita.processed_data p ON p.data_type_id = d.data_type_id " + "JOIN qiita.analysis_sample a ON p.processed_data_id = " + "a.processed_data_id WHERE a.analysis_id = %s ORDER BY " + "data_type") + conn_handler = SQLConnectionHandler() + return [x[0] for x in conn_handler.execute_fetchall(sql, (self._id, ))] + @property def shared_with(self): """The user the analysis is shared with @@ -325,28 +344,55 @@ def add_samples(self, samples): """ conn_handler = SQLConnectionHandler() self._lock_check(conn_handler) - sql = ("INSERT INTO qiita.analysis_sample (analysis_id, sample_id, " "processed_data_id) VALUES (%s, %s, %s)") conn_handler.executemany(sql, [(self._id, s[1], s[0]) for s in samples]) - def remove_samples(self, samples): + def remove_samples(self, proc_data=None, samples=None): """Removes samples from the analysis Parameters ---------- - samples : list of tuples - samples and the processed data id they come from in form - [(processed_data_id, sample_id), ...] + proc_data : list, optional + processed data ids to remove, default None + samples : list, optional + sample ids to remove, default None + + Notes + ----- + When only a list of samples given, the samples will be removed from all + processed data ids it is associated with + + When only a list of proc_data given, all samples associated with that + processed data are removed + + If both are passed, the given samples are removed from the given + processed data ids """ conn_handler = SQLConnectionHandler() self._lock_check(conn_handler) - - sql = ("DELETE FROM qiita.analysis_sample WHERE analysis_id = %s AND " - "sample_id = %s AND processed_data_id = %s") - conn_handler.executemany(sql, [(self._id, s[1], s[0]) - for s in samples]) + if proc_data and samples: + sql = ("DELETE FROM qiita.analysis_sample WHERE analysis_id = %s " + "AND processed_data_id = %s AND sample_id = %s") + remove = [] + # build tuples for what samples to remove from what processed data + for proc_id in proc_data: + for sample_id in samples: + remove.append((self._id, proc_id, sample_id)) + elif proc_data: + sql = ("DELETE FROM qiita.analysis_sample WHERE analysis_id = %s " + "AND processed_data_id = %s") + remove = [(self._id, p) for p in proc_data] + elif samples: + sql = ("DELETE FROM qiita.analysis_sample WHERE analysis_id = %s " + "AND sample_id = %s") + remove = [(self._id, s) for s in samples] + else: + raise IncompetentQiitaDeveloperError( + "Must provide list of samples and/or proc_data for removal!") + + conn_handler.executemany(sql, remove) def add_biom_tables(self, tables): """Adds biom tables to the analysis diff --git a/qiita_db/data.py b/qiita_db/data.py index 650aac799..100e87274 100644 --- a/qiita_db/data.py +++ b/qiita_db/data.py @@ -241,7 +241,7 @@ def create(cls, filetype, filepaths, studies): @property def studies(self): - r"""The list of study ids to which the raw data belongs to + r"""The IDs of the studies to which this raw data belongs Returns ------- @@ -393,7 +393,7 @@ def raw_data(self): @property def study(self): - r"""The study id to which this preprocessed data belongs to + r"""The ID of the study to which this preprocessed data belongs Returns ------- @@ -447,6 +447,7 @@ class ProcessedData(BaseData): Attributes ---------- preprocessed_data + study Methods ------- @@ -574,6 +575,20 @@ def preprocessed_data(self): "processed_data_id=%s".format(self._preprocessed_processed_table), [self._id])[0] + @property + def study(self): + r"""The ID of the study to which this processed data belongs + + Returns + ------- + int + The study id to which this processed data belongs""" + conn_handler = SQLConnectionHandler() + return conn_handler.execute_fetchone( + "SELECT study_id FROM qiita.{0} WHERE " + "processed_data_id=%s".format(self._study_processed_table), + [self._id])[0] + def data_type(self, ret_id=False): """Returns the data_type or data_type_id diff --git a/qiita_db/metadata_template.py b/qiita_db/metadata_template.py index 9a1516ad7..cc0530829 100644 --- a/qiita_db/metadata_template.py +++ b/qiita_db/metadata_template.py @@ -925,6 +925,23 @@ class SampleTemplate(MetadataTemplate): _id_column = "study_id" _sample_cls = Sample + @staticmethod + def metadata_headers(): + """Returns metadata headers available + + Returns + ------- + list + Alphabetical list of all metadata headers available + """ + conn_handler = SQLConnectionHandler() + return [x[0] for x in + conn_handler.execute_fetchall( + "SELECT DISTINCT column_name FROM qiita.study_sample_columns " + "UNION SELECT column_name FROM information_schema.columns " + "WHERE table_name = 'required_sample_info' " + "ORDER BY column_name")] + class PrepTemplate(MetadataTemplate): r"""Represent the PrepTemplate of a raw dat. Provides access to the diff --git a/qiita_db/search.py b/qiita_db/search.py index 8bb373d43..9a51baf74 100644 --- a/qiita_db/search.py +++ b/qiita_db/search.py @@ -112,24 +112,25 @@ def __repr__(self): class SearchTerm(object): # column names from required_sample_info table - required_cols = None + required_cols = set(get_table_cols("required_sample_info")) + # column names from study table + study_cols = set(get_table_cols("study")) def __init__(self, tokens): self.term = tokens[0] # clean all the inputs for pos, term in enumerate(self.term): self.term[pos] = scrub_data(term) - # create set of columns if needed - if not self.required_cols: - self.required_cols = set(get_table_cols("required_sample_info")) def generate_sql(self): # we can assume that the metadata is either in required_sample_info # or the study-specific table if self.term[0] in self.required_cols: self.term[0] = "r.%s" % self.term[0].lower() + elif self.term[0] in self.study_cols: + self.term[0] = "st.%s" % self.term[0].lower() else: - self.term[0] = "s.%s" % self.term[0].lower() + self.term[0] = "sa.%s" % self.term[0].lower() if self.term[1] == "includes": # substring search, so create proper query for it @@ -151,11 +152,9 @@ class QiitaStudySearch(object): """QiitaStudySearch object to parse and run searches on studies.""" # column names from required_sample_info table - required_cols = None - - def __init__(self): - if not self.required_cols: - self.required_cols = set(get_table_cols("required_sample_info")) + required_cols = set(get_table_cols("required_sample_info")) + # column names from study table + study_cols = set(get_table_cols("study")) def __call__(self, searchstr, user): """Runs a Study query and returns matching studies and samples @@ -196,8 +195,10 @@ def __call__(self, searchstr, user): results = {} # run search on each study to get out the matching samples for sid in study_ids: - results[sid] = conn_handler.execute_fetchall( - sample_sql.format(sid)) + study_res = conn_handler.execute_fetchall(sample_sql.format(sid)) + if study_res: + # only add study to results if actually has samples in results + results[sid] = study_res return results, meta_headers def _parse_study_search_string(self, searchstr): @@ -227,9 +228,9 @@ def _parse_study_search_string(self, searchstr): """ # build the parse grammar category = Word(alphas + nums + "_") - seperator = oneOf("> < = >= <=") | CaselessLiteral("includes") | \ + seperator = oneOf("> < = >= <= !=") | CaselessLiteral("includes") | \ CaselessLiteral("startswith") - value = Word(alphas + nums + "_" + ":") | \ + value = Word(alphas + nums + "_" + ":" + ".") | \ dblQuotedString().setParseAction(removeQuotes) criterion = Group(category + seperator + value) criterion.setParseAction(SearchTerm) @@ -259,13 +260,18 @@ def _parse_study_search_string(self, searchstr): # create the study finding SQL # remove metadata headers that are in required_sample_info table - meta_headers = meta_headers.difference(self.required_cols) + meta_headers = meta_headers.difference(self.required_cols).difference( + self.study_cols) # get all study ids that contain all metadata categories searched for sql = [] - for meta in meta_headers: - sql.append("SELECT study_id FROM qiita.study_sample_columns WHERE " - "column_name = '%s'" % - scrub_data(meta)) + if meta_headers: + # have study-specific metadata, so need to find specific studies + for meta in meta_headers: + sql.append("SELECT study_id FROM qiita.study_sample_columns " + "WHERE column_name = '%s'" % scrub_data(meta)) + else: + # no study-specific metadata, so need all studies + sql.append("SELECT study_id FROM qiita.study_sample_columns") # combine the query study_sql = ' INTERSECT '.join(sql) @@ -275,11 +281,15 @@ def _parse_study_search_string(self, searchstr): for meta in all_headers: if meta in self.required_cols: header_info.append("r.%s" % meta) + elif meta in self.study_cols: + header_info.append("st.%s" % meta) else: - header_info.append("s.%s" % meta) + header_info.append("sa.%s" % meta) # build the SQL query sample_sql = ("SELECT r.sample_id,%s FROM qiita.required_sample_info " - "r JOIN qiita.sample_{0} s ON s.sample_id = r.sample_id " - "WHERE %s" % (','.join(header_info), sql_where)) + "r JOIN qiita.sample_{0} sa ON sa.sample_id = " + "r.sample_id JOIN qiita.study st ON st.study_id = " + "r.study_id WHERE %s" % + (','.join(header_info), sql_where)) return study_sql, sample_sql, all_headers diff --git a/qiita_db/study.py b/qiita_db/study.py index dce536bd5..e41854912 100644 --- a/qiita_db/study.py +++ b/qiita_db/study.py @@ -103,7 +103,7 @@ from qiita_core.exceptions import IncompetentQiitaDeveloperError from .base import QiitaStatusObject, QiitaObject from .exceptions import (QiitaDBStatusError, QiitaDBColumnError) -from .util import check_required_columns, check_table_cols +from .util import check_required_columns, check_table_cols, convert_to_id from .sql_connection import SQLConnectionHandler @@ -120,12 +120,12 @@ class Study(QiitaStatusObject): pmids investigation sample_template - raw_data - preprocessed_data - processed_data Methods ------- + raw_data + preprocessed_data + processed_data add_pmid Notes @@ -438,48 +438,72 @@ def data_types(self): "DT.data_type_id WHERE SRD.study_id = %s") return [x[0] for x in conn_handler.execute_fetchall(sql, (self._id,))] - @property - def raw_data(self): + # --- methods --- + def raw_data(self, data_type=None): """ Returns list of data ids for raw data info + Parameters + ---------- + data_type : str, optional + If given, retrieve only raw_data for given datatype. Default None. + Returns ------- list of RawData ids """ + spec_data = "" + if data_type: + spec_data = " AND data_type_id = %d" % convert_to_id(data_type, + "data_type") conn_handler = SQLConnectionHandler() sql = ("SELECT raw_data_id FROM qiita.study_raw_data WHERE " - "study_id = %s") + "study_id = %s{0}".format(spec_data)) return [x[0] for x in conn_handler.execute_fetchall(sql, (self._id,))] - @property - def preprocessed_data(self): + def preprocessed_data(self, data_type=None): """ Returns list of data ids for preprocessed data info + Parameters + ---------- + data_type : str, optional + If given, retrieve only raw_data for given datatype. Default None. + Returns ------- list of PreprocessedData ids """ + spec_data = "" + if data_type: + spec_data = " AND data_type_id = %d" % convert_to_id(data_type, + "data_type") conn_handler = SQLConnectionHandler() sql = ("SELECT preprocessed_data_id FROM qiita.study_preprocessed_data" - " WHERE study_id = %s") + " WHERE study_id = %s{0}".format(spec_data)) return [x[0] for x in conn_handler.execute_fetchall(sql, (self._id,))] - @property - def processed_data(self): + def processed_data(self, data_type=None): """ Returns list of data ids for processed data info + Parameters + ---------- + data_type : str, optional + If given, retrieve only for given datatype. Default None. + Returns ------- list of ProcessedData ids """ + spec_data = "" + if data_type: + spec_data = " AND p.data_type_id = %d" % convert_to_id(data_type, + "data_type") conn_handler = SQLConnectionHandler() - sql = ("SELECT processed_data_id FROM " - "qiita.preprocessed_processed_data WHERE " - "preprocessed_data_id IN (SELECT preprocessed_data_id FROM " - "qiita.study_preprocessed_data where study_id = %s)") + sql = ("SELECT p.processed_data_id FROM qiita.processed_data p JOIN " + "qiita.study_processed_data sp ON p.processed_data_id = " + "sp.processed_data_id WHERE " + "sp.study_id = %s{0}".format(spec_data)) return [x[0] for x in conn_handler.execute_fetchall(sql, (self._id,))] -# --- methods --- def add_pmid(self, pmid): """Adds PMID to study diff --git a/qiita_db/support_files/populate_test_db.sql b/qiita_db/support_files/populate_test_db.sql index d6a685ee5..a53c26805 100644 --- a/qiita_db/support_files/populate_test_db.sql +++ b/qiita_db/support_files/populate_test_db.sql @@ -308,6 +308,9 @@ INSERT INTO qiita.preprocessed_sequence_illumina_params (trim_length) VALUES (15 -- Insert processed information for study 0 and processed data 1 INSERT INTO qiita.processed_data (processed_params_table, processed_params_id, processed_date, data_type_id) VALUES ('processed_params_uclust', 1, 'Mon Oct 1 09:30:27 2012', 2); +-- Insert (link) processed information to study 1 +INSERT INTO qiita.study_processed_data (processed_data_id, study_id) VALUES (1, 1); + -- Link the processed data with the preprocessed data INSERT INTO qiita.preprocessed_processed_data (preprocessed_data_id, processed_data_id) VALUES (1, 1); diff --git a/qiita_db/test/test_analysis.py b/qiita_db/test/test_analysis.py index e76385732..a066cdc0e 100644 --- a/qiita_db/test/test_analysis.py +++ b/qiita_db/test/test_analysis.py @@ -76,6 +76,10 @@ def test_retrieve_samples(self): 'SKM9.640192', 'SKM4.640180']} self.assertEqual(self.analysis.samples, exp) + def test_retrieve_data_types(self): + exp = ['18S'] + self.assertEqual(self.analysis.data_types, exp) + def test_retrieve_shared_with(self): self.assertEqual(self.analysis.shared_with, ["shared@foo.bar"]) @@ -116,10 +120,25 @@ def test_set_pmid(self): def test_add_samples(self): new = Analysis.create(User("admin@foo.bar"), "newAnalysis", "A New Analysis") - new.add_samples([(1, 'SKB8.640193')]) + new.add_samples([(1, 'SKB8.640193'), (1, 'SKD5.640186')]) + exp = {1: ['SKB8.640193', 'SKD5.640186']} + self.assertEqual(new.samples, exp) + + def test_remove_samples_both(self): + self.analysis.remove_samples(proc_data=(1, ), + samples=('SKB8.640193', )) + exp = {1: ['SKD8.640184', 'SKB7.640196', 'SKM9.640192', 'SKM4.640180']} + self.assertEqual(self.analysis.samples, exp) - def test_remove_samples(self): - self.analysis.remove_samples([(1, 'SKB8.640193'), (1, 'SKD8.640184')]) + def test_remove_samples_samples(self): + self.analysis.remove_samples(samples=('SKD8.640184', )) + exp = {1: ['SKB8.640193', 'SKB7.640196', 'SKM9.640192', 'SKM4.640180']} + self.assertEqual(self.analysis.samples, exp) + + def test_remove_samples_processed_data(self): + self.analysis.remove_samples(proc_data=(1, )) + exp = {} + self.assertEqual(self.analysis.samples, exp) def test_add_biom_tables(self): new = Analysis.create(User("admin@foo.bar"), "newAnalysis", diff --git a/qiita_db/test/test_search.py b/qiita_db/test/test_search.py index 4296c303d..61bf6aae4 100644 --- a/qiita_db/test/test_search.py +++ b/qiita_db/test/test_search.py @@ -24,9 +24,10 @@ def test_parse_study_search_string(self): self.search._parse_study_search_string("altitude > 0") exp_st_sql = ("SELECT study_id FROM qiita.study_sample_columns WHERE " "column_name = 'altitude'") - exp_samp_sql = ("SELECT r.sample_id,s.altitude FROM " - "qiita.required_sample_info r JOIN qiita.sample_{0} s " - "ON s.sample_id = r.sample_id WHERE s.altitude > 0") + exp_samp_sql = ("SELECT r.sample_id,sa.altitude FROM " + "qiita.required_sample_info r JOIN qiita.sample_{0} sa" + " ON sa.sample_id = r.sample_id JOIN qiita.study st ON" + " st.study_id = r.study_id WHERE sa.altitude > 0") self.assertEqual(st_sql, exp_st_sql) self.assertEqual(samp_sql, exp_samp_sql) self.assertEqual(meta, ["altitude"]) @@ -36,10 +37,11 @@ def test_parse_study_search_string(self): self.search._parse_study_search_string("NOT altitude > 0") exp_st_sql = ("SELECT study_id FROM qiita.study_sample_columns WHERE " "column_name = 'altitude'") - exp_samp_sql = ("SELECT r.sample_id,s.altitude FROM " - "qiita.required_sample_info r JOIN qiita.sample_{0} s " - "ON s.sample_id = r.sample_id WHERE NOT " - "s.altitude > 0") + exp_samp_sql = ("SELECT r.sample_id,sa.altitude FROM " + "qiita.required_sample_info r JOIN qiita.sample_{0} sa" + " ON sa.sample_id = r.sample_id JOIN qiita.study st ON" + " st.study_id = r.study_id WHERE NOT " + "sa.altitude > 0") self.assertEqual(st_sql, exp_st_sql) self.assertEqual(samp_sql, exp_samp_sql) self.assertEqual(meta, ["altitude"]) @@ -49,10 +51,11 @@ def test_parse_study_search_string(self): self.search._parse_study_search_string("ph > 7 and ph < 9") exp_st_sql = ("SELECT study_id FROM qiita.study_sample_columns WHERE " "column_name = 'ph'") - exp_samp_sql = ("SELECT r.sample_id,s.ph FROM " - "qiita.required_sample_info r JOIN qiita.sample_{0} s " - "ON s.sample_id = r.sample_id WHERE (s.ph > 7 AND " - "s.ph < 9)") + exp_samp_sql = ("SELECT r.sample_id,sa.ph FROM " + "qiita.required_sample_info r JOIN qiita.sample_{0} sa" + " ON sa.sample_id = r.sample_id JOIN qiita.study st ON" + " st.study_id = r.study_id WHERE (sa.ph > 7 AND " + "sa.ph < 9)") self.assertEqual(st_sql, exp_st_sql) self.assertEqual(samp_sql, exp_samp_sql) self.assertEqual(meta, ["ph"]) @@ -62,10 +65,11 @@ def test_parse_study_search_string(self): self.search._parse_study_search_string("ph > 7 or ph < 9") exp_st_sql = ("SELECT study_id FROM qiita.study_sample_columns WHERE " "column_name = 'ph'") - exp_samp_sql = ("SELECT r.sample_id,s.ph FROM " - "qiita.required_sample_info r JOIN qiita.sample_{0} s " - "ON s.sample_id = r.sample_id WHERE (s.ph > 7 OR " - "s.ph < 9)") + exp_samp_sql = ("SELECT r.sample_id,sa.ph FROM " + "qiita.required_sample_info r JOIN qiita.sample_{0} sa" + " ON sa.sample_id = r.sample_id JOIN qiita.study st ON" + " st.study_id = r.study_id WHERE (sa.ph > 7 OR " + "sa.ph < 9)") self.assertEqual(st_sql, exp_st_sql) self.assertEqual(samp_sql, exp_samp_sql) self.assertEqual(meta, ["ph"]) @@ -74,11 +78,12 @@ def test_parse_study_search_string(self): st_sql, samp_sql, meta = \ self.search._parse_study_search_string( 'host_subject_id includes "Chicken little"') - exp_st_sql = "" + exp_st_sql = "SELECT study_id FROM qiita.study_sample_columns" exp_samp_sql = ("SELECT r.sample_id,r.host_subject_id FROM " - "qiita.required_sample_info r JOIN qiita.sample_{0} s " - "ON s.sample_id = r.sample_id WHERE r.host_subject_id " - "LIKE '%Chicken little%'") + "qiita.required_sample_info r JOIN qiita.sample_{0} sa" + " ON sa.sample_id = r.sample_id JOIN qiita.study st ON" + " st.study_id = r.study_id WHERE " + "r.host_subject_id LIKE '%Chicken little%'") self.assertEqual(st_sql, exp_st_sql) self.assertEqual(samp_sql, exp_samp_sql) self.assertEqual(meta, ["host_subject_id"]) @@ -92,10 +97,11 @@ def test_parse_study_search_string(self): "SELECT study_id FROM qiita.study_sample_columns WHERE " "column_name = 'name'") exp_samp_sql = ( - "SELECT r.sample_id,s.name FROM qiita.required_sample_info r JOIN " - "qiita.sample_{0} s ON s.sample_id = r.sample_id WHERE (s.name = " - "'Billy Bob' OR s.name = 'Timmy' OR (s.name = 'Jimbo' AND " - "s.name > 25) OR s.name < 5)") + "SELECT r.sample_id,sa.name FROM qiita.required_sample_info r JOIN" + " qiita.sample_{0} sa ON sa.sample_id = r.sample_id JOIN " + "qiita.study st ON st.study_id = r.study_id WHERE (sa.name = " + "'Billy Bob' OR sa.name = 'Timmy' OR (sa.name = 'Jimbo' AND " + "sa.name > 25) OR sa.name < 5)") self.assertEqual(st_sql, exp_st_sql) self.assertEqual(samp_sql, exp_samp_sql) self.assertEqual(meta, ['name']) @@ -109,10 +115,11 @@ def test_parse_study_search_string(self): exp_st_sql = ["SELECT study_id FROM qiita.study_sample_columns WHERE " "column_name = 'pH'", "SELECT study_id FROM " "qiita.study_sample_columns WHERE column_name = 'ph'"] - exp_samp_sql = ("SELECT r.sample_id,s.pH,s.ph FROM " - "qiita.required_sample_info r JOIN qiita.sample_{0} s " - "ON s.sample_id = r.sample_id WHERE (s.ph > 7 OR " - "s.ph < 9)") + exp_samp_sql = ("SELECT r.sample_id,sa.pH,sa.ph FROM " + "qiita.required_sample_info r JOIN qiita.sample_{0} sa" + " ON sa.sample_id = r.sample_id JOIN qiita.study st ON" + " st.study_id = r.study_id WHERE (sa.ph > 7 OR " + "sa.ph < 9)") # use the split list to make sure the SQL is properly formed self.assertEqual(len(st_sql), 2) pos = exp_st_sql.index(st_sql[0]) @@ -151,6 +158,14 @@ def test_call_bad_meta_category(self): self.assertEqual(obs_res, {}) self.assertEqual(obs_meta, ["BAD_NAME_THING"]) + def test_call_no_results(self): + """makes sure a call on a required sample ID column that has no results + actually returns no results""" + obs_res, obs_meta = self.search('sample_type = unicorns_and_rainbows', + 'test@foo.bar') + self.assertEqual(obs_res, {}) + self.assertEqual(obs_meta, ['sample_type']) + if __name__ == "__main__": main() diff --git a/qiita_db/test/test_study.py b/qiita_db/test/test_study.py index 487a7147d..3444fa32c 100644 --- a/qiita_db/test/test_study.py +++ b/qiita_db/test/test_study.py @@ -387,28 +387,28 @@ def test_retrieve_data_types_none(self): self.assertEqual(new.data_types, []) def test_retrieve_raw_data(self): - self.assertEqual(self.study.raw_data, [1, 2]) + self.assertEqual(self.study.raw_data(), [1, 2]) def test_retrieve_raw_data_none(self): new = Study.create(User('test@foo.bar'), 'Identification of the ' 'Microbiomes for Cannabis Soils', [1], self.info) - self.assertEqual(new.raw_data, []) + self.assertEqual(new.raw_data(), []) def test_retrieve_preprocessed_data(self): - self.assertEqual(self.study.preprocessed_data, [1, 2]) + self.assertEqual(self.study.preprocessed_data(), [1, 2]) def test_retrieve_preprocessed_data_none(self): new = Study.create(User('test@foo.bar'), 'Identification of the ' 'Microbiomes for Cannabis Soils', [1], self.info) - self.assertEqual(new.preprocessed_data, []) + self.assertEqual(new.preprocessed_data(), []) def test_retrieve_processed_data(self): - self.assertEqual(self.study.processed_data, [1]) + self.assertEqual(self.study.processed_data(), [1]) def test_retrieve_processed_data_none(self): new = Study.create(User('test@foo.bar'), 'Identification of the ' 'Microbiomes for Cannabis Soils', [1], self.info) - self.assertEqual(new.processed_data, []) + self.assertEqual(new.processed_data(), []) def test_add_pmid(self): self.study.add_pmid('4544444') diff --git a/qiita_pet/handlers/analysis_handlers.py b/qiita_pet/handlers/analysis_handlers.py index 9cd1c9bb1..eb85477f0 100644 --- a/qiita_pet/handlers/analysis_handlers.py +++ b/qiita_pet/handlers/analysis_handlers.py @@ -10,12 +10,15 @@ # The full license is in the file LICENSE, distributed with this software. # ----------------------------------------------------------------------------- from __future__ import division +from future.utils import viewitems from tempfile import mkstemp from os import close from os.path import join +from itertools import product -from tornado.web import authenticated, asynchronous -from collections import defaultdict +from tornado.web import authenticated, asynchronous, HTTPError +from collections import defaultdict, Counter +from pyparsing import ParseException from qiita_pet.handlers.base_handlers import BaseHandler from qiita_ware.run import run_analysis @@ -25,7 +28,8 @@ from qiita_db.data import ProcessedData from qiita_db.metadata_template import SampleTemplate from qiita_db.job import Job, Command -from qiita_db.util import get_db_files_base_dir +from qiita_db.util import get_db_files_base_dir, get_table_cols +from qiita_db.search import QiitaStudySearch def check_analysis_access(user, analysis_id): @@ -48,69 +52,170 @@ def check_analysis_access(user, analysis_id): raise RuntimeError("Analysis access denied to %s" % (analysis_id)) -class CreateAnalysisHandler(BaseHandler): - """Analysis creation""" +class SearchStudiesHandler(BaseHandler): + def _parse_search_results(self, results, selsamples, meta_headers): + """remove already selected samples from results and count metadata""" + counts = {} + fullcounts = {meta: defaultdict(int) for meta in meta_headers} + for study, samples in viewitems(results): + counts[study] = {meta: Counter() + for meta in meta_headers} + topop = [] + for pos, sample in enumerate(samples): + if study in selsamples and sample[0] in selsamples[study]: + topop.append(pos) + # still add to full counts, but not study counts + for pos, meta in enumerate(meta_headers): + fullcounts[meta][sample[pos+1]] += 1 + else: + for pos, meta in enumerate(meta_headers): + counts[study][meta][sample[pos+1]] += 1 + fullcounts[meta][sample[pos+1]] += 1 + # remove already selected samples + topop.sort(reverse=True) + for pos in topop: + samples.pop(pos) + return results, counts, fullcounts + + def _selected_parser(self, analysis): + """builds dictionaries of selected samples from analysis object""" + selsamples = {} + selproc_data = defaultdict(list) + for proc_data_id, samps in viewitems(analysis.samples): + study = ProcessedData(proc_data_id).study + selproc_data[study].append(proc_data_id) + selsamples[study] = set(samps) + return selproc_data, selsamples + + def _parse_form_select(self): + """parses selected checkboxes and yields the selected ones in + format accepted by Analysis.add_samples() + """ + # get the selected studies and datatypes for studies + studyinfo = self.get_arguments("availstudies") + for s in studyinfo: + study_id, datatype = s.split("#") + # get the processed data ids for the study + # get new selected samples for each study and yield with proc id + for proc_samp_combo in product(self.get_arguments(s), + self.get_arguments(study_id)): + yield proc_samp_combo + + def _parse_form_deselect(self): + """parses selected checkboxes and returns the selected ones in + format accepted by Analysis.remove_samples() + """ + studyinfo = self.get_arguments("selstudies") + proc_data = [] + samples = [] + for sid in studyinfo: + # get the processed data ids and add it to the study + proc_data.extend(self.get_arguments("dt%s" % sid)) + # get new selected samples for each study and add to study + samples.extend(self.get_arguments("sel%s" % sid)) + return proc_data, samples + @authenticated def get(self): - self.render('create_analysis.html', user=self.current_user) - + user = self.current_user + analysis = Analysis(int(self.get_argument("aid"))) + # make sure user has access to the analysis + userobj = User(user) + try: + check_analysis_access(userobj, analysis.id) + except RuntimeError: + # trying to access someone else's analysis, so throw 403 error + raise HTTPError(403) + # get the dictionaries of selected samples and data types + selproc_data, selsamples = self._selected_parser(analysis) + + self.render('search_studies.html', user=user, aid=analysis.id, + selsamples=selsamples, selproc_data=selproc_data, + counts={}, fullcounts={}, searchmsg="", query="", + results={}, availmeta=SampleTemplate.metadata_headers() + + get_table_cols("study")) -class SelectStudiesHandler(BaseHandler): - """Study selection""" @authenticated def post(self): - name = self.get_argument('name') - description = self.get_argument('description') user = self.current_user - userobj = User(user) - # create list of studies - study_ids = Study.get_public() + userobj.private_studies + \ - userobj.shared_studies - - studies = [Study(i) for i in study_ids] - analysis = Analysis.create(User(user), name, description) - - self.render('select_studies.html', user=user, aid=analysis.id, - studies=studies) + action = self.get_argument("action") + # set required template variables + results = {} + meta_headers = [] + counts = {} + fullcounts = {} + query = "" + searchmsg = "" + selsamples = {} + selproc_data = {} + # get analysis and selected samples if exists, or create if necessary + if action == "create": + name = self.get_argument('name') + description = self.get_argument('description') + analysis = Analysis.create(User(user), name, description) + aid = analysis.id + # fill example studies by running query for specific studies + search = QiitaStudySearch() + def_query = 'study_id = 1 OR study_id = 2 OR study_id = 3' + results, meta_headers = search(def_query, user) + results, counts, fullcounts = self._parse_search_results( + results, selsamples, meta_headers) + else: + aid = int(self.get_argument("analysis-id")) + analysis = Analysis(aid) + selproc_data, selsamples = self._selected_parser(analysis) + + # run through action requested + if action == "search": + search = QiitaStudySearch() + query = str(self.get_argument("query")) + try: + results, meta_headers = search(query, user) + except ParseException: + searchmsg = "Malformed search query, please read search help." + + if not results and not searchmsg: + searchmsg = "No results found." + else: + results, counts, fullcounts = self._parse_search_results( + results, selsamples, meta_headers) + + elif action == "select": + analysis.add_samples(self._parse_form_select()) + + # rebuild the selected from database to reflect changes + selproc_data, selsamples = self._selected_parser(analysis) + + elif action == "deselect": + proc_data, samples = self._parse_form_deselect() + analysis.remove_samples(proc_data=proc_data) + analysis.remove_samples(samples=samples) + + # rebuild the selected from database to reflect changes + selproc_data, selsamples = self._selected_parser(analysis) + + self.render('search_studies.html', user=user, aid=aid, results=results, + meta_headers=meta_headers, selsamples=selsamples, + selproc_data=selproc_data, counts=counts, + fullcounts=fullcounts, searchmsg=searchmsg, query=query, + availmeta=SampleTemplate.metadata_headers() + + get_table_cols("study")) class SelectCommandsHandler(BaseHandler): """Select commands to be executed""" @authenticated def post(self): - analysis_id = self.get_argument('analysis-id') - study_args = self.get_arguments('studies') - split = [x.split("#") for x in study_args] - - # build dictionary of studies and datatypes selected - # as well a set of unique datatypes selected - study_dts = defaultdict(list) - data_types = set() - for study_id, data_type in split: - study_dts[study_id].append(data_type) - data_types.add(data_type) - + analysis = Analysis(int(self.get_argument('analysis-id'))) + data_types = analysis.data_types # sort the elements to have 16S be the first tho show on the tabs - data_types = sorted(list(data_types)) + data_types.sort() # FIXME: Pull out from the database, see #111 commands = Command.get_commands_by_datatype() self.render('select_commands.html', user=self.current_user, - commands=commands, data_types=data_types, aid=analysis_id) - - analysis = Analysis(analysis_id) - - for study_id in study_dts: - study = Study(study_id) - processed_data = {ProcessedData(pid).data_type: pid for pid in - study.processed_data} - - sample_ids = SampleTemplate(study.id).keys() - for data_type in study_dts[study.id]: - samples = [(processed_data[data_type], sid) for sid in - sample_ids] - analysis.add_samples(samples) + commands=commands, data_types=data_types, aid=analysis.id) class AnalysisWaitHandler(BaseHandler): @@ -189,6 +294,7 @@ def get(self, aid): class ShowAnalysesHandler(BaseHandler): """Shows the user's analyses""" + @authenticated def get(self): user_id = self.current_user user = User(user_id) diff --git a/qiita_pet/static/css/style.css b/qiita_pet/static/css/style.css index 797bcaf77..e939b8e38 100644 --- a/qiita_pet/static/css/style.css +++ b/qiita_pet/static/css/style.css @@ -1,5 +1,6 @@ #template-content{ - padding: 20px; + padding: 10px; height: 100%; + width: 100%; } diff --git a/qiita_pet/static/js/analysis.js b/qiita_pet/static/js/analysis.js new file mode 100644 index 000000000..f1fe7d0e7 --- /dev/null +++ b/qiita_pet/static/js/analysis.js @@ -0,0 +1,49 @@ +function displaySelected() { + var linktext = document.getElementById("shselected"); + var seldiv = document.getElementById("selected"); + var sampdiv = document.getElementById("availstudies"); + var buttondiv = document.getElementById("seperator"); + if(seldiv.style.display == "none") { + sampdiv.style.bottom = "30%"; + sampdiv.style.height='60%'; + buttondiv.style.bottom = "25%"; + seldiv.style.height = "25%"; + seldiv.style.display = ""; + linktext.innerHTML = "Hide selected samples"; + } + else { + sampdiv.style.bottom = ""; + sampdiv.style.height='90%'; + buttondiv.style.bottom = "0px"; + seldiv.style.height = "0px"; + seldiv.style.display = "none"; + linktext.innerHTML = "Show selected samples"; + } +} + +function select_category(category, study) { + if(study != '') { + $('.'+study+'.'+category).each(function() {this.checked = true;}); + } + else { + $('.'+category).each(function() {this.checked = true;}); + } +} + +function select_deselect(study, select) { + if(select == true) { + $('.'+study).each(function() {this.checked = true;}); + } + else { + $('.'+study).each(function() {this.checked = false;}); + } +} + +function enable_study_datatype(id) { + document.getElementById(id).disabled=false; +} + +function pre_submit(action) { + document.getElementById('action').value = action; + if(action == 'continue') {document.getElementById('results-form').action = '/analysis/3'} +} diff --git a/qiita_pet/static/vendor/css/chosen-sprite.png b/qiita_pet/static/vendor/css/chosen-sprite.png new file mode 100755 index 000000000..3611ae4ac Binary files /dev/null and b/qiita_pet/static/vendor/css/chosen-sprite.png differ diff --git a/qiita_pet/static/vendor/css/chosen-sprite@2x.png b/qiita_pet/static/vendor/css/chosen-sprite@2x.png new file mode 100755 index 000000000..ffe4d7d11 Binary files /dev/null and b/qiita_pet/static/vendor/css/chosen-sprite@2x.png differ diff --git a/qiita_pet/static/vendor/css/chosen.css b/qiita_pet/static/vendor/css/chosen.css new file mode 100755 index 000000000..63b2c4a76 --- /dev/null +++ b/qiita_pet/static/vendor/css/chosen.css @@ -0,0 +1,435 @@ +/*! +Chosen, a Select Box Enhancer for jQuery and Prototype +by Patrick Filler for Harvest, http://getharvest.com + +Version 1.1.0 +Full source at https://github.com/harvesthq/chosen +Copyright (c) 2011 Harvest http://getharvest.com + +MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md +This file is generated by `grunt build`, do not edit it by hand. +*/ + +/* @group Base */ +.chosen-container { + position: relative; + display: inline-block; + vertical-align: middle; + font-size: 13px; + zoom: 1; + *display: inline; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} +.chosen-container .chosen-drop { + position: absolute; + top: 100%; + left: -9999px; + z-index: 1010; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + width: 100%; + border: 1px solid #aaa; + border-top: 0; + background: #fff; + box-shadow: 0 4px 5px rgba(0, 0, 0, 0.15); +} +.chosen-container.chosen-with-drop .chosen-drop { + left: 0; +} +.chosen-container a { + cursor: pointer; +} + +/* @end */ +/* @group Single Chosen */ +.chosen-container-single .chosen-single { + position: relative; + display: block; + overflow: hidden; + padding: 0 0 0 8px; + height: 23px; + border: 1px solid #aaa; + border-radius: 5px; + background-color: #fff; + background: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #ffffff), color-stop(50%, #f6f6f6), color-stop(52%, #eeeeee), color-stop(100%, #f4f4f4)); + background: -webkit-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); + background: -moz-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); + background: -o-linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); + background: linear-gradient(top, #ffffff 20%, #f6f6f6 50%, #eeeeee 52%, #f4f4f4 100%); + background-clip: padding-box; + box-shadow: 0 0 3px white inset, 0 1px 1px rgba(0, 0, 0, 0.1); + color: #444; + text-decoration: none; + white-space: nowrap; + line-height: 24px; +} +.chosen-container-single .chosen-default { + color: #999; +} +.chosen-container-single .chosen-single span { + display: block; + overflow: hidden; + margin-right: 26px; + text-overflow: ellipsis; + white-space: nowrap; +} +.chosen-container-single .chosen-single-with-deselect span { + margin-right: 38px; +} +.chosen-container-single .chosen-single abbr { + position: absolute; + top: 6px; + right: 26px; + display: block; + width: 12px; + height: 12px; + background: url('chosen-sprite.png') -42px 1px no-repeat; + font-size: 1px; +} +.chosen-container-single .chosen-single abbr:hover { + background-position: -42px -10px; +} +.chosen-container-single.chosen-disabled .chosen-single abbr:hover { + background-position: -42px -10px; +} +.chosen-container-single .chosen-single div { + position: absolute; + top: 0; + right: 0; + display: block; + width: 18px; + height: 100%; +} +.chosen-container-single .chosen-single div b { + display: block; + width: 100%; + height: 100%; + background: url('chosen-sprite.png') no-repeat 0px 2px; +} +.chosen-container-single .chosen-search { + position: relative; + z-index: 1010; + margin: 0; + padding: 3px 4px; + white-space: nowrap; +} +.chosen-container-single .chosen-search input[type="text"] { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin: 1px 0; + padding: 4px 20px 4px 5px; + width: 100%; + height: auto; + outline: 0; + border: 1px solid #aaa; + background: white url('chosen-sprite.png') no-repeat 100% -20px; + background: url('chosen-sprite.png') no-repeat 100% -20px; + font-size: 1em; + font-family: sans-serif; + line-height: normal; + border-radius: 0; +} +.chosen-container-single .chosen-drop { + margin-top: -1px; + border-radius: 0 0 4px 4px; + background-clip: padding-box; +} +.chosen-container-single.chosen-container-single-nosearch .chosen-search { + position: absolute; + left: -9999px; +} + +/* @end */ +/* @group Results */ +.chosen-container .chosen-results { + position: relative; + overflow-x: hidden; + overflow-y: auto; + margin: 0 4px 4px 0; + padding: 0 0 0 4px; + max-height: 240px; + -webkit-overflow-scrolling: touch; +} +.chosen-container .chosen-results li { + display: none; + margin: 0; + padding: 5px 6px; + list-style: none; + line-height: 15px; + -webkit-touch-callout: none; +} +.chosen-container .chosen-results li.active-result { + display: list-item; + cursor: pointer; +} +.chosen-container .chosen-results li.disabled-result { + display: list-item; + color: #ccc; + cursor: default; +} +.chosen-container .chosen-results li.highlighted { + background-color: #3875d7; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #3875d7), color-stop(90%, #2a62bc)); + background-image: -webkit-linear-gradient(#3875d7 20%, #2a62bc 90%); + background-image: -moz-linear-gradient(#3875d7 20%, #2a62bc 90%); + background-image: -o-linear-gradient(#3875d7 20%, #2a62bc 90%); + background-image: linear-gradient(#3875d7 20%, #2a62bc 90%); + color: #fff; +} +.chosen-container .chosen-results li.no-results { + display: list-item; + background: #f4f4f4; +} +.chosen-container .chosen-results li.group-result { + display: list-item; + font-weight: bold; + cursor: default; +} +.chosen-container .chosen-results li.group-option { + padding-left: 15px; +} +.chosen-container .chosen-results li em { + font-style: normal; + text-decoration: underline; +} + +/* @end */ +/* @group Multi Chosen */ +.chosen-container-multi .chosen-choices { + position: relative; + overflow: hidden; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + margin: 0; + padding: 0; + width: 100%; + height: auto !important; + height: 1%; + border: 1px solid #aaa; + background-color: #fff; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(1%, #eeeeee), color-stop(15%, #ffffff)); + background-image: -webkit-linear-gradient(#eeeeee 1%, #ffffff 15%); + background-image: -moz-linear-gradient(#eeeeee 1%, #ffffff 15%); + background-image: -o-linear-gradient(#eeeeee 1%, #ffffff 15%); + background-image: linear-gradient(#eeeeee 1%, #ffffff 15%); + cursor: text; +} +.chosen-container-multi .chosen-choices li { + float: left; + list-style: none; +} +.chosen-container-multi .chosen-choices li.search-field { + margin: 0; + padding: 0; + white-space: nowrap; +} +.chosen-container-multi .chosen-choices li.search-field input[type="text"] { + margin: 1px 0; + padding: 5px; + height: 15px; + outline: 0; + border: 0 !important; + background: transparent !important; + box-shadow: none; + color: #666; + font-size: 100%; + font-family: sans-serif; + line-height: normal; + border-radius: 0; +} +.chosen-container-multi .chosen-choices li.search-field .default { + color: #999; +} +.chosen-container-multi .chosen-choices li.search-choice { + position: relative; + margin: 3px 0 3px 5px; + padding: 3px 20px 3px 5px; + border: 1px solid #aaa; + border-radius: 3px; + background-color: #e4e4e4; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); + background-image: -webkit-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -moz-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -o-linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: linear-gradient(#f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-clip: padding-box; + box-shadow: 0 0 2px white inset, 0 1px 0 rgba(0, 0, 0, 0.05); + color: #333; + line-height: 13px; + cursor: default; +} +.chosen-container-multi .chosen-choices li.search-choice .search-choice-close { + position: absolute; + top: 4px; + right: 3px; + display: block; + width: 12px; + height: 12px; + background: url('chosen-sprite.png') -42px 1px no-repeat; + font-size: 1px; +} +.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover { + background-position: -42px -10px; +} +.chosen-container-multi .chosen-choices li.search-choice-disabled { + padding-right: 5px; + border: 1px solid #ccc; + background-color: #e4e4e4; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eeeeee)); + background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: -o-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eeeeee 100%); + color: #666; +} +.chosen-container-multi .chosen-choices li.search-choice-focus { + background: #d4d4d4; +} +.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close { + background-position: -42px -10px; +} +.chosen-container-multi .chosen-results { + margin: 0; + padding: 0; +} +.chosen-container-multi .chosen-drop .result-selected { + display: list-item; + color: #ccc; + cursor: default; +} + +/* @end */ +/* @group Active */ +.chosen-container-active .chosen-single { + border: 1px solid #5897fb; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); +} +.chosen-container-active.chosen-with-drop .chosen-single { + border: 1px solid #aaa; + -moz-border-radius-bottomright: 0; + border-bottom-right-radius: 0; + -moz-border-radius-bottomleft: 0; + border-bottom-left-radius: 0; + background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(20%, #eeeeee), color-stop(80%, #ffffff)); + background-image: -webkit-linear-gradient(#eeeeee 20%, #ffffff 80%); + background-image: -moz-linear-gradient(#eeeeee 20%, #ffffff 80%); + background-image: -o-linear-gradient(#eeeeee 20%, #ffffff 80%); + background-image: linear-gradient(#eeeeee 20%, #ffffff 80%); + box-shadow: 0 1px 0 #fff inset; +} +.chosen-container-active.chosen-with-drop .chosen-single div { + border-left: none; + background: transparent; +} +.chosen-container-active.chosen-with-drop .chosen-single div b { + background-position: -18px 2px; +} +.chosen-container-active .chosen-choices { + border: 1px solid #5897fb; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); +} +.chosen-container-active .chosen-choices li.search-field input[type="text"] { + color: #111 !important; +} + +/* @end */ +/* @group Disabled Support */ +.chosen-disabled { + opacity: 0.5 !important; + cursor: default; +} +.chosen-disabled .chosen-single { + cursor: default; +} +.chosen-disabled .chosen-choices .search-choice .search-choice-close { + cursor: default; +} + +/* @end */ +/* @group Right to Left */ +.chosen-rtl { + text-align: right; +} +.chosen-rtl .chosen-single { + overflow: visible; + padding: 0 8px 0 0; +} +.chosen-rtl .chosen-single span { + margin-right: 0; + margin-left: 26px; + direction: rtl; +} +.chosen-rtl .chosen-single-with-deselect span { + margin-left: 38px; +} +.chosen-rtl .chosen-single div { + right: auto; + left: 3px; +} +.chosen-rtl .chosen-single abbr { + right: auto; + left: 26px; +} +.chosen-rtl .chosen-choices li { + float: right; +} +.chosen-rtl .chosen-choices li.search-field input[type="text"] { + direction: rtl; +} +.chosen-rtl .chosen-choices li.search-choice { + margin: 3px 5px 3px 0; + padding: 3px 5px 3px 19px; +} +.chosen-rtl .chosen-choices li.search-choice .search-choice-close { + right: auto; + left: 4px; +} +.chosen-rtl.chosen-container-single-nosearch .chosen-search, +.chosen-rtl .chosen-drop { + left: 9999px; +} +.chosen-rtl.chosen-container-single .chosen-results { + margin: 0 0 4px 4px; + padding: 0 4px 0 0; +} +.chosen-rtl .chosen-results li.group-option { + padding-right: 15px; + padding-left: 0; +} +.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div { + border-right: none; +} +.chosen-rtl .chosen-search input[type="text"] { + padding: 4px 5px 4px 20px; + background: white url('chosen-sprite.png') no-repeat -30px -20px; + background: url('chosen-sprite.png') no-repeat -30px -20px; + direction: rtl; +} +.chosen-rtl.chosen-container-single .chosen-single div b { + background-position: 6px 2px; +} +.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b { + background-position: -12px 2px; +} + +/* @end */ +/* @group Retina compatibility */ +@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-resolution: 144dpi) { + .chosen-rtl .chosen-search input[type="text"], + .chosen-container-single .chosen-single abbr, + .chosen-container-single .chosen-single div b, + .chosen-container-single .chosen-search input[type="text"], + .chosen-container-multi .chosen-choices .search-choice .search-choice-close, + .chosen-container .chosen-results-scroll-down span, + .chosen-container .chosen-results-scroll-up span { + background-image: url('chosen-sprite@2x.png') !important; + background-size: 52px 37px !important; + background-repeat: no-repeat !important; + } +} +/* @end */ diff --git a/qiita_pet/static/vendor/js/chosen.jquery.min.js b/qiita_pet/static/vendor/js/chosen.jquery.min.js new file mode 100755 index 000000000..cece231bd --- /dev/null +++ b/qiita_pet/static/vendor/js/chosen.jquery.min.js @@ -0,0 +1,2 @@ +/* Chosen v1.1.0 | (c) 2011-2013 by Harvest | MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md */ +!function(){var a,AbstractChosen,Chosen,SelectParser,b,c={}.hasOwnProperty,d=function(a,b){function d(){this.constructor=a}for(var e in b)c.call(b,e)&&(a[e]=b[e]);return d.prototype=b.prototype,a.prototype=new d,a.__super__=b.prototype,a};SelectParser=function(){function SelectParser(){this.options_index=0,this.parsed=[]}return SelectParser.prototype.add_node=function(a){return"OPTGROUP"===a.nodeName.toUpperCase()?this.add_group(a):this.add_option(a)},SelectParser.prototype.add_group=function(a){var b,c,d,e,f,g;for(b=this.parsed.length,this.parsed.push({array_index:b,group:!0,label:this.escapeExpression(a.label),children:0,disabled:a.disabled}),f=a.childNodes,g=[],d=0,e=f.length;e>d;d++)c=f[d],g.push(this.add_option(c,b,a.disabled));return g},SelectParser.prototype.add_option=function(a,b,c){return"OPTION"===a.nodeName.toUpperCase()?(""!==a.text?(null!=b&&(this.parsed[b].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:a.value,text:a.text,html:a.innerHTML,selected:a.selected,disabled:c===!0?c:a.disabled,group_array_index:b,classes:a.className,style:a.style.cssText})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0}),this.options_index+=1):void 0},SelectParser.prototype.escapeExpression=function(a){var b,c;return null==a||a===!1?"":/[\&\<\>\"\'\`]/.test(a)?(b={"<":"<",">":">",'"':""","'":"'","`":"`"},c=/&(?!\w+;)|[\<\>\"\'\`]/g,a.replace(c,function(a){return b[a]||"&"})):a},SelectParser}(),SelectParser.select_to_array=function(a){var b,c,d,e,f;for(c=new SelectParser,f=a.childNodes,d=0,e=f.length;e>d;d++)b=f[d],c.add_node(b);return c.parsed},AbstractChosen=function(){function AbstractChosen(a,b){this.form_field=a,this.options=null!=b?b:{},AbstractChosen.browser_is_supported()&&(this.is_multiple=this.form_field.multiple,this.set_default_text(),this.set_default_values(),this.setup(),this.set_up_html(),this.register_observers())}return AbstractChosen.prototype.set_default_values=function(){var a=this;return this.click_test_action=function(b){return a.test_active_click(b)},this.activate_action=function(b){return a.activate_field(b)},this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.allow_single_deselect=null!=this.options.allow_single_deselect&&null!=this.form_field.options[0]&&""===this.form_field.options[0].text?this.options.allow_single_deselect:!1,this.disable_search_threshold=this.options.disable_search_threshold||0,this.disable_search=this.options.disable_search||!1,this.enable_split_word_search=null!=this.options.enable_split_word_search?this.options.enable_split_word_search:!0,this.group_search=null!=this.options.group_search?this.options.group_search:!0,this.search_contains=this.options.search_contains||!1,this.single_backstroke_delete=null!=this.options.single_backstroke_delete?this.options.single_backstroke_delete:!0,this.max_selected_options=this.options.max_selected_options||1/0,this.inherit_select_classes=this.options.inherit_select_classes||!1,this.display_selected_options=null!=this.options.display_selected_options?this.options.display_selected_options:!0,this.display_disabled_options=null!=this.options.display_disabled_options?this.options.display_disabled_options:!0},AbstractChosen.prototype.set_default_text=function(){return this.default_text=this.form_field.getAttribute("data-placeholder")?this.form_field.getAttribute("data-placeholder"):this.is_multiple?this.options.placeholder_text_multiple||this.options.placeholder_text||AbstractChosen.default_multiple_text:this.options.placeholder_text_single||this.options.placeholder_text||AbstractChosen.default_single_text,this.results_none_found=this.form_field.getAttribute("data-no_results_text")||this.options.no_results_text||AbstractChosen.default_no_result_text},AbstractChosen.prototype.mouse_enter=function(){return this.mouse_on_container=!0},AbstractChosen.prototype.mouse_leave=function(){return this.mouse_on_container=!1},AbstractChosen.prototype.input_focus=function(){var a=this;if(this.is_multiple){if(!this.active_field)return setTimeout(function(){return a.container_mousedown()},50)}else if(!this.active_field)return this.activate_field()},AbstractChosen.prototype.input_blur=function(){var a=this;return this.mouse_on_container?void 0:(this.active_field=!1,setTimeout(function(){return a.blur_test()},100))},AbstractChosen.prototype.results_option_build=function(a){var b,c,d,e,f;for(b="",f=this.results_data,d=0,e=f.length;e>d;d++)c=f[d],b+=c.group?this.result_add_group(c):this.result_add_option(c),(null!=a?a.first:void 0)&&(c.selected&&this.is_multiple?this.choice_build(c):c.selected&&!this.is_multiple&&this.single_set_selected_text(c.text));return b},AbstractChosen.prototype.result_add_option=function(a){var b,c;return a.search_match?this.include_option_in_results(a)?(b=[],a.disabled||a.selected&&this.is_multiple||b.push("active-result"),!a.disabled||a.selected&&this.is_multiple||b.push("disabled-result"),a.selected&&b.push("result-selected"),null!=a.group_array_index&&b.push("group-option"),""!==a.classes&&b.push(a.classes),c=document.createElement("li"),c.className=b.join(" "),c.style.cssText=a.style,c.setAttribute("data-option-array-index",a.array_index),c.innerHTML=a.search_text,this.outerHTML(c)):"":""},AbstractChosen.prototype.result_add_group=function(a){var b;return a.search_match||a.group_match?a.active_options>0?(b=document.createElement("li"),b.className="group-result",b.innerHTML=a.search_text,this.outerHTML(b)):"":""},AbstractChosen.prototype.results_update_field=function(){return this.set_default_text(),this.is_multiple||this.results_reset_cleanup(),this.result_clear_highlight(),this.results_build(),this.results_showing?this.winnow_results():void 0},AbstractChosen.prototype.reset_single_select_options=function(){var a,b,c,d,e;for(d=this.results_data,e=[],b=0,c=d.length;c>b;b++)a=d[b],a.selected?e.push(a.selected=!1):e.push(void 0);return e},AbstractChosen.prototype.results_toggle=function(){return this.results_showing?this.results_hide():this.results_show()},AbstractChosen.prototype.results_search=function(){return this.results_showing?this.winnow_results():this.results_show()},AbstractChosen.prototype.winnow_results=function(){var a,b,c,d,e,f,g,h,i,j,k,l,m;for(this.no_results_clear(),e=0,g=this.get_search_text(),a=g.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),d=this.search_contains?"":"^",c=new RegExp(d+a,"i"),j=new RegExp(a,"i"),m=this.results_data,k=0,l=m.length;l>k;k++)b=m[k],b.search_match=!1,f=null,this.include_option_in_results(b)&&(b.group&&(b.group_match=!1,b.active_options=0),null!=b.group_array_index&&this.results_data[b.group_array_index]&&(f=this.results_data[b.group_array_index],0===f.active_options&&f.search_match&&(e+=1),f.active_options+=1),(!b.group||this.group_search)&&(b.search_text=b.group?b.label:b.html,b.search_match=this.search_string_match(b.search_text,c),b.search_match&&!b.group&&(e+=1),b.search_match?(g.length&&(h=b.search_text.search(j),i=b.search_text.substr(0,h+g.length)+""+b.search_text.substr(h+g.length),b.search_text=i.substr(0,h)+""+i.substr(h)),null!=f&&(f.group_match=!0)):null!=b.group_array_index&&this.results_data[b.group_array_index].search_match&&(b.search_match=!0)));return this.result_clear_highlight(),1>e&&g.length?(this.update_results_content(""),this.no_results(g)):(this.update_results_content(this.results_option_build()),this.winnow_results_set_highlight())},AbstractChosen.prototype.search_string_match=function(a,b){var c,d,e,f;if(b.test(a))return!0;if(this.enable_split_word_search&&(a.indexOf(" ")>=0||0===a.indexOf("["))&&(d=a.replace(/\[|\]/g,"").split(" "),d.length))for(e=0,f=d.length;f>e;e++)if(c=d[e],b.test(c))return!0},AbstractChosen.prototype.choices_count=function(){var a,b,c,d;if(null!=this.selected_option_count)return this.selected_option_count;for(this.selected_option_count=0,d=this.form_field.options,b=0,c=d.length;c>b;b++)a=d[b],a.selected&&(this.selected_option_count+=1);return this.selected_option_count},AbstractChosen.prototype.choices_click=function(a){return a.preventDefault(),this.results_showing||this.is_disabled?void 0:this.results_show()},AbstractChosen.prototype.keyup_checker=function(a){var b,c;switch(b=null!=(c=a.which)?c:a.keyCode,this.search_field_scale(),b){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices_count()>0)return this.keydown_backstroke();if(!this.pending_backstroke)return this.result_clear_highlight(),this.results_search();break;case 13:if(a.preventDefault(),this.results_showing)return this.result_select(a);break;case 27:return this.results_showing&&this.results_hide(),!0;case 9:case 38:case 40:case 16:case 91:case 17:break;default:return this.results_search()}},AbstractChosen.prototype.clipboard_event_checker=function(){var a=this;return setTimeout(function(){return a.results_search()},50)},AbstractChosen.prototype.container_width=function(){return null!=this.options.width?this.options.width:""+this.form_field.offsetWidth+"px"},AbstractChosen.prototype.include_option_in_results=function(a){return this.is_multiple&&!this.display_selected_options&&a.selected?!1:!this.display_disabled_options&&a.disabled?!1:a.empty?!1:!0},AbstractChosen.prototype.search_results_touchstart=function(a){return this.touch_started=!0,this.search_results_mouseover(a)},AbstractChosen.prototype.search_results_touchmove=function(a){return this.touch_started=!1,this.search_results_mouseout(a)},AbstractChosen.prototype.search_results_touchend=function(a){return this.touch_started?this.search_results_mouseup(a):void 0},AbstractChosen.prototype.outerHTML=function(a){var b;return a.outerHTML?a.outerHTML:(b=document.createElement("div"),b.appendChild(a),b.innerHTML)},AbstractChosen.browser_is_supported=function(){return"Microsoft Internet Explorer"===window.navigator.appName?document.documentMode>=8:/iP(od|hone)/i.test(window.navigator.userAgent)?!1:/Android/i.test(window.navigator.userAgent)&&/Mobile/i.test(window.navigator.userAgent)?!1:!0},AbstractChosen.default_multiple_text="Select Some Options",AbstractChosen.default_single_text="Select an Option",AbstractChosen.default_no_result_text="No results match",AbstractChosen}(),a=jQuery,a.fn.extend({chosen:function(b){return AbstractChosen.browser_is_supported()?this.each(function(){var c,d;c=a(this),d=c.data("chosen"),"destroy"===b&&d?d.destroy():d||c.data("chosen",new Chosen(this,b))}):this}}),Chosen=function(c){function Chosen(){return b=Chosen.__super__.constructor.apply(this,arguments)}return d(Chosen,c),Chosen.prototype.setup=function(){return this.form_field_jq=a(this.form_field),this.current_selectedIndex=this.form_field.selectedIndex,this.is_rtl=this.form_field_jq.hasClass("chosen-rtl")},Chosen.prototype.set_up_html=function(){var b,c;return b=["chosen-container"],b.push("chosen-container-"+(this.is_multiple?"multi":"single")),this.inherit_select_classes&&this.form_field.className&&b.push(this.form_field.className),this.is_rtl&&b.push("chosen-rtl"),c={"class":b.join(" "),style:"width: "+this.container_width()+";",title:this.form_field.title},this.form_field.id.length&&(c.id=this.form_field.id.replace(/[^\w]/g,"_")+"_chosen"),this.container=a("
",c),this.is_multiple?this.container.html('
    '):this.container.html(''+this.default_text+'
      '),this.form_field_jq.hide().after(this.container),this.dropdown=this.container.find("div.chosen-drop").first(),this.search_field=this.container.find("input").first(),this.search_results=this.container.find("ul.chosen-results").first(),this.search_field_scale(),this.search_no_results=this.container.find("li.no-results").first(),this.is_multiple?(this.search_choices=this.container.find("ul.chosen-choices").first(),this.search_container=this.container.find("li.search-field").first()):(this.search_container=this.container.find("div.chosen-search").first(),this.selected_item=this.container.find(".chosen-single").first()),this.results_build(),this.set_tab_index(),this.set_label_behavior(),this.form_field_jq.trigger("chosen:ready",{chosen:this})},Chosen.prototype.register_observers=function(){var a=this;return this.container.bind("mousedown.chosen",function(b){a.container_mousedown(b)}),this.container.bind("mouseup.chosen",function(b){a.container_mouseup(b)}),this.container.bind("mouseenter.chosen",function(b){a.mouse_enter(b)}),this.container.bind("mouseleave.chosen",function(b){a.mouse_leave(b)}),this.search_results.bind("mouseup.chosen",function(b){a.search_results_mouseup(b)}),this.search_results.bind("mouseover.chosen",function(b){a.search_results_mouseover(b)}),this.search_results.bind("mouseout.chosen",function(b){a.search_results_mouseout(b)}),this.search_results.bind("mousewheel.chosen DOMMouseScroll.chosen",function(b){a.search_results_mousewheel(b)}),this.search_results.bind("touchstart.chosen",function(b){a.search_results_touchstart(b)}),this.search_results.bind("touchmove.chosen",function(b){a.search_results_touchmove(b)}),this.search_results.bind("touchend.chosen",function(b){a.search_results_touchend(b)}),this.form_field_jq.bind("chosen:updated.chosen",function(b){a.results_update_field(b)}),this.form_field_jq.bind("chosen:activate.chosen",function(b){a.activate_field(b)}),this.form_field_jq.bind("chosen:open.chosen",function(b){a.container_mousedown(b)}),this.form_field_jq.bind("chosen:close.chosen",function(b){a.input_blur(b)}),this.search_field.bind("blur.chosen",function(b){a.input_blur(b)}),this.search_field.bind("keyup.chosen",function(b){a.keyup_checker(b)}),this.search_field.bind("keydown.chosen",function(b){a.keydown_checker(b)}),this.search_field.bind("focus.chosen",function(b){a.input_focus(b)}),this.search_field.bind("cut.chosen",function(b){a.clipboard_event_checker(b)}),this.search_field.bind("paste.chosen",function(b){a.clipboard_event_checker(b)}),this.is_multiple?this.search_choices.bind("click.chosen",function(b){a.choices_click(b)}):this.container.bind("click.chosen",function(a){a.preventDefault()})},Chosen.prototype.destroy=function(){return a(this.container[0].ownerDocument).unbind("click.chosen",this.click_test_action),this.search_field[0].tabIndex&&(this.form_field_jq[0].tabIndex=this.search_field[0].tabIndex),this.container.remove(),this.form_field_jq.removeData("chosen"),this.form_field_jq.show()},Chosen.prototype.search_field_disabled=function(){return this.is_disabled=this.form_field_jq[0].disabled,this.is_disabled?(this.container.addClass("chosen-disabled"),this.search_field[0].disabled=!0,this.is_multiple||this.selected_item.unbind("focus.chosen",this.activate_action),this.close_field()):(this.container.removeClass("chosen-disabled"),this.search_field[0].disabled=!1,this.is_multiple?void 0:this.selected_item.bind("focus.chosen",this.activate_action))},Chosen.prototype.container_mousedown=function(b){return this.is_disabled||(b&&"mousedown"===b.type&&!this.results_showing&&b.preventDefault(),null!=b&&a(b.target).hasClass("search-choice-close"))?void 0:(this.active_field?this.is_multiple||!b||a(b.target)[0]!==this.selected_item[0]&&!a(b.target).parents("a.chosen-single").length||(b.preventDefault(),this.results_toggle()):(this.is_multiple&&this.search_field.val(""),a(this.container[0].ownerDocument).bind("click.chosen",this.click_test_action),this.results_show()),this.activate_field())},Chosen.prototype.container_mouseup=function(a){return"ABBR"!==a.target.nodeName||this.is_disabled?void 0:this.results_reset(a)},Chosen.prototype.search_results_mousewheel=function(a){var b;return a.originalEvent&&(b=-a.originalEvent.wheelDelta||a.originalEvent.detail),null!=b?(a.preventDefault(),"DOMMouseScroll"===a.type&&(b=40*b),this.search_results.scrollTop(b+this.search_results.scrollTop())):void 0},Chosen.prototype.blur_test=function(){return!this.active_field&&this.container.hasClass("chosen-container-active")?this.close_field():void 0},Chosen.prototype.close_field=function(){return a(this.container[0].ownerDocument).unbind("click.chosen",this.click_test_action),this.active_field=!1,this.results_hide(),this.container.removeClass("chosen-container-active"),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale()},Chosen.prototype.activate_field=function(){return this.container.addClass("chosen-container-active"),this.active_field=!0,this.search_field.val(this.search_field.val()),this.search_field.focus()},Chosen.prototype.test_active_click=function(b){var c;return c=a(b.target).closest(".chosen-container"),c.length&&this.container[0]===c[0]?this.active_field=!0:this.close_field()},Chosen.prototype.results_build=function(){return this.parsing=!0,this.selected_option_count=null,this.results_data=SelectParser.select_to_array(this.form_field),this.is_multiple?this.search_choices.find("li.search-choice").remove():this.is_multiple||(this.single_set_selected_text(),this.disable_search||this.form_field.options.length<=this.disable_search_threshold?(this.search_field[0].readOnly=!0,this.container.addClass("chosen-container-single-nosearch")):(this.search_field[0].readOnly=!1,this.container.removeClass("chosen-container-single-nosearch"))),this.update_results_content(this.results_option_build({first:!0})),this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.parsing=!1},Chosen.prototype.result_do_highlight=function(a){var b,c,d,e,f;if(a.length){if(this.result_clear_highlight(),this.result_highlight=a,this.result_highlight.addClass("highlighted"),d=parseInt(this.search_results.css("maxHeight"),10),f=this.search_results.scrollTop(),e=d+f,c=this.result_highlight.position().top+this.search_results.scrollTop(),b=c+this.result_highlight.outerHeight(),b>=e)return this.search_results.scrollTop(b-d>0?b-d:0);if(f>c)return this.search_results.scrollTop(c)}},Chosen.prototype.result_clear_highlight=function(){return this.result_highlight&&this.result_highlight.removeClass("highlighted"),this.result_highlight=null},Chosen.prototype.results_show=function(){return this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field_jq.trigger("chosen:maxselected",{chosen:this}),!1):(this.container.addClass("chosen-with-drop"),this.results_showing=!0,this.search_field.focus(),this.search_field.val(this.search_field.val()),this.winnow_results(),this.form_field_jq.trigger("chosen:showing_dropdown",{chosen:this}))},Chosen.prototype.update_results_content=function(a){return this.search_results.html(a)},Chosen.prototype.results_hide=function(){return this.results_showing&&(this.result_clear_highlight(),this.container.removeClass("chosen-with-drop"),this.form_field_jq.trigger("chosen:hiding_dropdown",{chosen:this})),this.results_showing=!1},Chosen.prototype.set_tab_index=function(){var a;return this.form_field.tabIndex?(a=this.form_field.tabIndex,this.form_field.tabIndex=-1,this.search_field[0].tabIndex=a):void 0},Chosen.prototype.set_label_behavior=function(){var b=this;return this.form_field_label=this.form_field_jq.parents("label"),!this.form_field_label.length&&this.form_field.id.length&&(this.form_field_label=a("label[for='"+this.form_field.id+"']")),this.form_field_label.length>0?this.form_field_label.bind("click.chosen",function(a){return b.is_multiple?b.container_mousedown(a):b.activate_field()}):void 0},Chosen.prototype.show_search_field_default=function(){return this.is_multiple&&this.choices_count()<1&&!this.active_field?(this.search_field.val(this.default_text),this.search_field.addClass("default")):(this.search_field.val(""),this.search_field.removeClass("default"))},Chosen.prototype.search_results_mouseup=function(b){var c;return c=a(b.target).hasClass("active-result")?a(b.target):a(b.target).parents(".active-result").first(),c.length?(this.result_highlight=c,this.result_select(b),this.search_field.focus()):void 0},Chosen.prototype.search_results_mouseover=function(b){var c;return c=a(b.target).hasClass("active-result")?a(b.target):a(b.target).parents(".active-result").first(),c?this.result_do_highlight(c):void 0},Chosen.prototype.search_results_mouseout=function(b){return a(b.target).hasClass("active-result")?this.result_clear_highlight():void 0},Chosen.prototype.choice_build=function(b){var c,d,e=this;return c=a("
    • ",{"class":"search-choice"}).html(""+b.html+""),b.disabled?c.addClass("search-choice-disabled"):(d=a("",{"class":"search-choice-close","data-option-array-index":b.array_index}),d.bind("click.chosen",function(a){return e.choice_destroy_link_click(a)}),c.append(d)),this.search_container.before(c)},Chosen.prototype.choice_destroy_link_click=function(b){return b.preventDefault(),b.stopPropagation(),this.is_disabled?void 0:this.choice_destroy(a(b.target))},Chosen.prototype.choice_destroy=function(a){return this.result_deselect(a[0].getAttribute("data-option-array-index"))?(this.show_search_field_default(),this.is_multiple&&this.choices_count()>0&&this.search_field.val().length<1&&this.results_hide(),a.parents("li").first().remove(),this.search_field_scale()):void 0},Chosen.prototype.results_reset=function(){return this.reset_single_select_options(),this.form_field.options[0].selected=!0,this.single_set_selected_text(),this.show_search_field_default(),this.results_reset_cleanup(),this.form_field_jq.trigger("change"),this.active_field?this.results_hide():void 0},Chosen.prototype.results_reset_cleanup=function(){return this.current_selectedIndex=this.form_field.selectedIndex,this.selected_item.find("abbr").remove()},Chosen.prototype.result_select=function(a){var b,c;return this.result_highlight?(b=this.result_highlight,this.result_clear_highlight(),this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field_jq.trigger("chosen:maxselected",{chosen:this}),!1):(this.is_multiple?b.removeClass("active-result"):this.reset_single_select_options(),c=this.results_data[b[0].getAttribute("data-option-array-index")],c.selected=!0,this.form_field.options[c.options_index].selected=!0,this.selected_option_count=null,this.is_multiple?this.choice_build(c):this.single_set_selected_text(c.text),(a.metaKey||a.ctrlKey)&&this.is_multiple||this.results_hide(),this.search_field.val(""),(this.is_multiple||this.form_field.selectedIndex!==this.current_selectedIndex)&&this.form_field_jq.trigger("change",{selected:this.form_field.options[c.options_index].value}),this.current_selectedIndex=this.form_field.selectedIndex,this.search_field_scale())):void 0},Chosen.prototype.single_set_selected_text=function(a){return null==a&&(a=this.default_text),a===this.default_text?this.selected_item.addClass("chosen-default"):(this.single_deselect_control_build(),this.selected_item.removeClass("chosen-default")),this.selected_item.find("span").text(a)},Chosen.prototype.result_deselect=function(a){var b;return b=this.results_data[a],this.form_field.options[b.options_index].disabled?!1:(b.selected=!1,this.form_field.options[b.options_index].selected=!1,this.selected_option_count=null,this.result_clear_highlight(),this.results_showing&&this.winnow_results(),this.form_field_jq.trigger("change",{deselected:this.form_field.options[b.options_index].value}),this.search_field_scale(),!0)},Chosen.prototype.single_deselect_control_build=function(){return this.allow_single_deselect?(this.selected_item.find("abbr").length||this.selected_item.find("span").first().after(''),this.selected_item.addClass("chosen-single-with-deselect")):void 0},Chosen.prototype.get_search_text=function(){return this.search_field.val()===this.default_text?"":a("
      ").text(a.trim(this.search_field.val())).html()},Chosen.prototype.winnow_results_set_highlight=function(){var a,b;return b=this.is_multiple?[]:this.search_results.find(".result-selected.active-result"),a=b.length?b.first():this.search_results.find(".active-result").first(),null!=a?this.result_do_highlight(a):void 0},Chosen.prototype.no_results=function(b){var c;return c=a('
    • '+this.results_none_found+' ""
    • '),c.find("span").first().html(b),this.search_results.append(c),this.form_field_jq.trigger("chosen:no_results",{chosen:this})},Chosen.prototype.no_results_clear=function(){return this.search_results.find(".no-results").remove()},Chosen.prototype.keydown_arrow=function(){var a;return this.results_showing&&this.result_highlight?(a=this.result_highlight.nextAll("li.active-result").first())?this.result_do_highlight(a):void 0:this.results_show()},Chosen.prototype.keyup_arrow=function(){var a;return this.results_showing||this.is_multiple?this.result_highlight?(a=this.result_highlight.prevAll("li.active-result"),a.length?this.result_do_highlight(a.first()):(this.choices_count()>0&&this.results_hide(),this.result_clear_highlight())):void 0:this.results_show()},Chosen.prototype.keydown_backstroke=function(){var a;return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.find("a").first()),this.clear_backstroke()):(a=this.search_container.siblings("li.search-choice").last(),a.length&&!a.hasClass("search-choice-disabled")?(this.pending_backstroke=a,this.single_backstroke_delete?this.keydown_backstroke():this.pending_backstroke.addClass("search-choice-focus")):void 0)},Chosen.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClass("search-choice-focus"),this.pending_backstroke=null},Chosen.prototype.keydown_checker=function(a){var b,c;switch(b=null!=(c=a.which)?c:a.keyCode,this.search_field_scale(),8!==b&&this.pending_backstroke&&this.clear_backstroke(),b){case 8:this.backstroke_length=this.search_field.val().length;break;case 9:this.results_showing&&!this.is_multiple&&this.result_select(a),this.mouse_on_container=!1;break;case 13:a.preventDefault();break;case 38:a.preventDefault(),this.keyup_arrow();break;case 40:a.preventDefault(),this.keydown_arrow()}},Chosen.prototype.search_field_scale=function(){var b,c,d,e,f,g,h,i,j;if(this.is_multiple){for(d=0,h=0,f="position:absolute; left: -1000px; top: -1000px; display:none;",g=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"],i=0,j=g.length;j>i;i++)e=g[i],f+=e+":"+this.search_field.css(e)+";";return b=a("
      ",{style:f}),b.text(this.search_field.val()),a("body").append(b),h=b.width()+25,b.remove(),c=this.container.outerWidth(),h>c-10&&(h=c-10),this.search_field.css({width:h+"px"})}},Chosen}(AbstractChosen)}.call(this); \ No newline at end of file diff --git a/qiita_pet/templates/search_studies.html b/qiita_pet/templates/search_studies.html new file mode 100644 index 000000000..88daed7e7 --- /dev/null +++ b/qiita_pet/templates/search_studies.html @@ -0,0 +1,234 @@ +{%extends sitebase.html%} +{%block head%} + + + + +{%end%} + +{%block content%} +{% from future.utils import viewitems %} +{% from qiita_db.study import Study %} +{% from qiita_db.data import ProcessedData %} +{% from qiita_pet.util import clean_str %} +
      + + + +{% for sid in selproc_data %} + + {% end %} +
      +
      + +Selected Samples + + + {% for sid, proc_ids in viewitems(selproc_data) %} + {% set study = Study(sid) %} + + {%end%} +
      DatatypesTitle
      + {% for proc in proc_ids %} + {% set data_type = ProcessedData(proc).data_type() %} + {{data_type}} - {{proc}}
      + {% end %} +
      {{study.title}}
      +
      + +
      +
      +

      Search

      +

      Search help | Metadata categories

      +

      +
      {{searchmsg}}
      + + +{% if results %} + Overall available samples for current search +
      + {% set divcount = 1 %} + {% for meta_cat, sub_cats in viewitems(fullcounts) %} +
      + +
      +
      + + {% for sub_cat, count in viewitems(sub_cats) %} + + + {% end %} +
      ValueSamples
      {{sub_cat}}{{count}}Select category
      +
      +
      +
      + {% set divcount += 1 %} + {% end %} +
      + + + + {% for sid, samples in viewitems(results) %} + {% set study = Study(sid) %} + + + + {% end %} + {% end %} +
      Data TypesNameDescriptionStatus
      + {% for data_type in study.data_types %} + + {{data_type}}
      +
      {{study.title}} + {{study.info["study_description"]}}{{study.status}}
      +
      +
      +
      + {% for sid, samples in viewitems(results) %} + + {% end %} +{% end %} +
      + + + +
      +
      + + +{%end%} \ No newline at end of file diff --git a/qiita_pet/templates/select_studies.html b/qiita_pet/templates/select_studies.html deleted file mode 100644 index 98c2f8592..000000000 --- a/qiita_pet/templates/select_studies.html +++ /dev/null @@ -1,40 +0,0 @@ -{% extends sitebase.html%} - -{%block head%} - -{% end %} - -{% block content %} - -