diff --git a/anvio/interactive.py b/anvio/interactive.py index d5b5db8820..0d6be744f0 100644 --- a/anvio/interactive.py +++ b/anvio/interactive.py @@ -11,7 +11,7 @@ import anvio.ccollections as ccollections import anvio.completeness as completeness -from anvio.dbops import ProfileSuperclass, ContigsSuperclass, SamplesInformationDatabase, TablesForStates +from anvio.dbops import ProfileSuperclass, ContigsSuperclass, SamplesInformationDatabase, TablesForStates, ProfileDatabase from anvio.dbops import is_profile_db_and_contigs_db_compatible, is_profile_db_and_samples_db_compatible from anvio.errors import ConfigError @@ -43,12 +43,13 @@ def __init__(self, args, external_clustering = None): self.title = 'Unknown Project' A = lambda x: args.__dict__[x] if args.__dict__.has_key(x) else None + self.profile_db_path = A('profile_db') + self.contigs_db_path = A('contigs_db') + self.manual_mode = A('manual_mode') self.state = A('state') self.split_hmm_layers = A('split_hmm_layers') self.additional_layers_path = A('additional_layers') self.additional_view_path = A('additional_view') - self.profile_db_path = A('profile_db') - self.contigs_db_path = A('contigs_db') self.samples_information_db_path = A('samples_information_db') self.view = A('view') self.fasta_file = A('fasta_file') @@ -94,22 +95,10 @@ def __init__(self, args, external_clustering = None): self.cwd = os.getcwd() # here is where the big deal stuff takes place: - if self.profile_db_path: - if not self.contigs_db_path: - raise ConfigError, "Anvi'o needs the contigs database to make sense of this run." - - ProfileSuperclass.__init__(self, args) - - # this is a weird place to do it, but we are going to ask ContigsSuperclass function to load - # all the split sequences since only now we know the mun_contig_length that was used to profile - # this stuff - self.init_split_sequences(self.p_meta['min_contig_length']) - - self.collections.populate_sources_dict(self.profile_db_path, anvio.__profile__version__) - - self.load_from_profile_database(args) + if self.manual_mode: + self.load_from_user_files(args) else: - self.load_from_files(args) + self.load_from_anvio_files(args) if self.external_clustering: self.p_meta['clusterings'] = self.clusterings = self.external_clustering['clusterings'] @@ -154,21 +143,45 @@ def __init__(self, args, external_clustering = None): self.convert_view_data_into_json() - def load_from_files(self, args): - if (not self.fasta_file) or (not self.view_data_path) or (not self.tree) or (not self.output_dir): - raise ConfigError, "If you do not have a RUNINFO dict, you must declare each of\ - '-f', '-d', '-t' and '-o' parameters. Please see '--help' for\ - more detailed information on them." + def load_from_user_files(self, args): + if self.contigs_db_path: + raise ConfigError, "When you want to use the interactive interface in an ad hoc manner, you must\ + not use a contigs database." + + if not self.profile_db_path: + raise ConfigError, "Even when you want to use the interactive interface in an ad hoc manner by\ + using the '--manual-mode' flag, you still need to declare a profile database.\ + The profile database in this mode only used to read or store the 'state' of\ + the display for visualization purposes. You DO NOT need to point to an already\ + existing database, as anvi'o will generate an empty one for your if there is no\ + profile database." + + if (not self.fasta_file) or (not self.view_data_path) or (not self.tree): + raise ConfigError, "When you are running the interactive interface in manual mode, you must declare\ + each of '-f', '-d', and '-t' parameters. Please see the help menu for more info." if self.view: - raise ConfigError, "You can't use '-v' parameter when this program is not called with a RUNINFO.cp" + raise ConfigError, "You can't use '--view' parameter when you are running the interactive interface\ + in manual mode" if self.show_views: - raise ConfigError, "Sorry, there are no views to show when there is no RUNINFO.cp :/" + raise ConfigError, "Sorry, there are no views to show in manual mode :/" + + + # create a new, empty profile database for ad hoc operations + if not os.path.exists(self.profile_db_path): + profile_db = ProfileDatabase(self.profile_db_path) + profile_db.create({'db_type': 'profile', 'contigs_db_hash': None}) + + # create an instance of states table + self.states_table = TablesForStates(self.profile_db_path, anvio.__profile__version__) + + # also populate collections, if there are any + self.collections.populate_sources_dict(self.profile_db_path, anvio.__profile__version__) view_data_path = os.path.abspath(self.view_data_path) self.p_meta['splits_fasta'] = os.path.abspath(self.fasta_file) - self.p_meta['output_dir'] = os.path.abspath(self.output_dir) + self.p_meta['output_dir'] = None self.p_meta['views'] = {} self.p_meta['default_view'] = 'single' self.p_meta['default_clustering'] = 'default' @@ -200,20 +213,22 @@ def load_from_files(self, args): self.splits_basic_info[split_id] = {'length': len(self.split_sequences[split_id]), 'gc_content': utils.get_GC_content_for_sequence(self.split_sequences[split_id])} - # reminder: this is being stored in the output dir provided as a commandline parameter: - self.p_meta['self_path'] = os.path.join(self.p_meta['output_dir'], 'RUNINFO.cp') - if self.title: self.title = self.title - filesnpaths.gen_output_directory(self.p_meta['output_dir']) + def load_from_anvio_files(self, args): + if not self.contigs_db_path: + raise ConfigError, "Anvi'o needs the contigs database to make sense of this run." + + ProfileSuperclass.__init__(self, args) + + # this is a weird place to do it, but we are going to ask ContigsSuperclass function to load + # all the split sequences since only now we know the mun_contig_length that was used to profile + # this stuff + self.init_split_sequences(self.p_meta['min_contig_length']) - def load_from_profile_database(self, args): - if self.p_meta['version'] != anvio.__profile__version__: - raise ConfigError, "The profile database has a version number that differs from the version that is valid\ - for this codebase (the profile database is at '%s', and the codebase is at '%s'). Very\ - unfortunately, you need to re-profile and re-merge this project using the current anvi'o :(" + self.collections.populate_sources_dict(self.profile_db_path, anvio.__profile__version__) self.p_meta['self_path'] = self.profile_db_path self.p_meta['output_dir'] = os.path.join(os.getcwd(), os.path.dirname(self.profile_db_path)) diff --git a/bin/anvi-interactive b/bin/anvi-interactive index 4a761bdcf4..afe16cdd13 100755 --- a/bin/anvi-interactive +++ b/bin/anvi-interactive @@ -49,7 +49,7 @@ parser = argparse.ArgumentParser(description="Start an anvi'o server for the int groupA = parser.add_argument_group('DEFAULT INPUTS', "The interavtive interface can be started with and without\ anvi'o databases. The default use assumes you have your\ profile and contigs database, however, it is also possible\ - to start the interface using ad-hoc input files. See 'MANUAL\ + to start the interface using ad hoc input files. See 'MANUAL\ INPUT' section for other set of parameters that are mutually\ exclusive with datanases.") groupB = parser.add_argument_group('MANUAL INPUTS', "Mandatory input parameters to start the interactive interface\ @@ -64,6 +64,7 @@ groupF = parser.add_argument_group('SERVER CONFIGURATION', "For power users.") groupA.add_argument(*anvio.A('profile-db'), **anvio.K('profile-db', {'required': False})) groupA.add_argument(*anvio.A('contigs-db'), **anvio.K('contigs-db', {'required': False})) groupA.add_argument(*anvio.A('samples-information-db'), **anvio.K('samples-information-db')) +groupB.add_argument(*anvio.A('manual-mode'), **anvio.K('manual-mode')) groupB.add_argument(*anvio.A('fasta-file'), **anvio.K('fasta-file')) groupB.add_argument(*anvio.A('view-data'), **anvio.K('view-data')) groupB.add_argument(*anvio.A('tree'), **anvio.K('tree')) @@ -289,11 +290,15 @@ def send_summary_static(collection_id, filename): @route('/summarize/') def gen_summary(collection_id): + set_default_headers(response) + if args.read_only: - return json.dumps("Sorry! This is a read-only instance.") + return json.dumps({'error': "Sorry! This is a read-only instance."}) + + if d.manual_mode: + return json.dumps({'error': "Creating summaries is only possible with proper anvi'o runs at the moment :/"}) run.info_single('A summary of collection "%s" has been requested.' % collection_id) - set_default_headers(response) class Args: pass diff --git a/tests/run_manual_interactive.sh b/tests/run_manual_interactive.sh index 2c75bef974..5454fa1073 100755 --- a/tests/run_manual_interactive.sh +++ b/tests/run_manual_interactive.sh @@ -15,4 +15,4 @@ INFO "Generating a newick file from the data ..." anvi-matrix-to-newick view_data.txt -o test-output/tree.txt INFO "Running the interactive interface on files" -anvi-interactive -f fasta.fa -d view_data.txt -A additional_view_data.txt -t test-output/tree.txt -o test-output/output-dir --title 'Interactive Tree For User Provided Files' +anvi-interactive -f fasta.fa -d view_data.txt -A additional_view_data.txt -t test-output/tree.txt --manual-mode -p test.db --title 'Interactive Tree For User Provided Files'