1212 level = logging .INFO ,
1313 format = "%(asctime)s - %(levelname)s - %(message)s" ,
1414 datefmt = "%H:%M:%S" ,
15- handlers = [logging .StreamHandler (sys .stderr )]
15+ handlers = [logging .StreamHandler (sys .stderr )],
1616)
1717logger = logging .getLogger ("MinkeMap" )
1818
19+
1920def main ():
20- parser = argparse .ArgumentParser (description = "MinkeMap: Circular Genome Visualization Tool" )
21-
21+ parser = argparse .ArgumentParser (
22+ description = "MinkeMap: Circular Genome Visualization Tool"
23+ )
24+
2225 # Meta
23- parser .add_argument ("-v" , "--version" , action = "version" , version = f"MinkeMap v{ __version__ } " )
24-
26+ parser .add_argument (
27+ "-v" , "--version" , action = "version" , version = f"MinkeMap v{ __version__ } "
28+ )
29+
2530 # Input / Output
26- parser .add_argument ("-r" , "--reference" , required = True , help = "Reference genome (FASTA or GenBank)" )
27- parser .add_argument ("-i" , "--input" , nargs = "+" , help = "Input sequencing files (FASTQ/FASTA)" )
28- parser .add_argument ("-f" , "--input-file" , help = "Manifest CSV file (cols: sample,read1,read2,type)" )
29- parser .add_argument ("-o" , "--output" , default = "minkemap_plot.png" , help = "Output filename" )
30- parser .add_argument ("--outdir" , default = "minkemap_results" , help = "Directory to save all output files" )
31-
31+ parser .add_argument (
32+ "-r" , "--reference" , required = True , help = "Reference genome (FASTA or GenBank)"
33+ )
34+ parser .add_argument (
35+ "-i" , "--input" , nargs = "+" , help = "Input sequencing files (FASTQ/FASTA)"
36+ )
37+ parser .add_argument (
38+ "-f" , "--input-file" , help = "Manifest CSV file (cols: sample,read1,read2,type)"
39+ )
40+ parser .add_argument (
41+ "-o" , "--output" , default = "minkemap_plot.png" , help = "Output filename"
42+ )
43+ parser .add_argument (
44+ "--outdir" ,
45+ default = "minkemap_results" ,
46+ help = "Directory to save all output files" ,
47+ )
48+
3249 # Aesthetics
33- parser .add_argument ("--track-width" , type = float , default = 6 , help = "Width of each track ring (default: 6)" )
34- parser .add_argument ("--track-gap" , type = float , default = 4 , help = "Gap between tracks (default: 4)" )
50+ parser .add_argument (
51+ "--track-width" ,
52+ type = float ,
53+ default = 6 ,
54+ help = "Width of each track ring (default: 6)" ,
55+ )
56+ parser .add_argument (
57+ "--track-gap" , type = float , default = 4 , help = "Gap between tracks (default: 4)"
58+ )
3559 parser .add_argument ("--palette" , default = "whale" , help = "Color palette" )
3660 parser .add_argument ("--dpi" , type = int , default = 300 , help = "Image resolution" )
3761 parser .add_argument ("--title" , help = "Plot title" )
38- parser .add_argument ("--no-legend" , action = "store_true" , help = "Hide the sample legend" )
39- parser .add_argument ("--no-backbone" , action = "store_true" , help = "Hide the black reference backbone" )
40- parser .add_argument ("--label-size" , type = int , default = 6 , help = "Font size for gene/annotation labels" )
41-
62+ parser .add_argument (
63+ "--no-legend" , action = "store_true" , help = "Hide the sample legend"
64+ )
65+ parser .add_argument (
66+ "--no-backbone" , action = "store_true" , help = "Hide the black reference backbone"
67+ )
68+ parser .add_argument (
69+ "--label-size" , type = int , default = 6 , help = "Font size for gene/annotation labels"
70+ )
71+
4272 # Features
43- parser .add_argument ("--annotations" , help = "CSV file for custom regions (cols: reference,start,stop,label,color)" )
44- parser .add_argument ("--highlights" , help = "CSV file for background wedges (cols: start,end,color,label)" )
45- parser .add_argument ("--gc-skew" , action = "store_true" , help = "Add a GC Skew track to the center" )
46- parser .add_argument ("--no-save-data" , action = "store_true" , help = "Do not generate BED/CSV data files" )
47-
73+ parser .add_argument (
74+ "--annotations" ,
75+ help = "CSV file for custom regions (cols: reference,start,stop,label,color)" ,
76+ )
77+ parser .add_argument (
78+ "--highlights" ,
79+ help = "CSV file for background wedges (cols: start,end,color,label)" ,
80+ )
81+ parser .add_argument (
82+ "--gc-skew" , action = "store_true" , help = "Add a GC Skew track to the center"
83+ )
84+ parser .add_argument (
85+ "--no-save-data" , action = "store_true" , help = "Do not generate BED/CSV data files"
86+ )
87+
4888 # Filters
49- parser .add_argument ("--min-identity" , type = float , default = 0 , help = "Minimum identity %% (0-100)" )
50- parser .add_argument ("--min-coverage" , type = float , default = 0 , help = "Minimum query coverage %% (0-100)" )
51- parser .add_argument ("--exclude-genes" , help = "Comma-separated list of terms to exclude from gene track (e.g. 'hypothetical,putative')" )
89+ parser .add_argument (
90+ "--min-identity" , type = float , default = 0 , help = "Minimum identity %% (0-100)"
91+ )
92+ parser .add_argument (
93+ "--min-coverage" ,
94+ type = float ,
95+ default = 0 ,
96+ help = "Minimum query coverage %% (0-100)" ,
97+ )
98+ parser .add_argument (
99+ "--exclude-genes" ,
100+ help = "Comma-separated list of terms to exclude from gene track (e.g. 'hypothetical,putative')" ,
101+ )
52102 parser .add_argument ("--verbose" , action = "store_true" , help = "Enable debug logging" )
53103
54104 args = parser .parse_args ()
55-
105+
56106 if args .verbose :
57107 logger .setLevel (logging .DEBUG )
58108
59109 min_id_decimal = args .min_identity / 100.0
60110 min_cov_decimal = args .min_coverage / 100.0
61111 should_save = not args .no_save_data
62-
112+
63113 # 1. Create Output Directory & Log File
64114 try :
65115 os .makedirs (args .outdir , exist_ok = True )
66116 # Add File Handler to log to disk
67117 log_path = os .path .join (args .outdir , "minkemap.log" )
68- file_handler = logging .FileHandler (log_path , mode = 'w' )
69- file_handler .setFormatter (logging .Formatter ("%(asctime)s - %(levelname)s - %(message)s" ))
118+ file_handler = logging .FileHandler (log_path , mode = "w" )
119+ file_handler .setFormatter (
120+ logging .Formatter ("%(asctime)s - %(levelname)s - %(message)s" )
121+ )
70122 logger .addHandler (file_handler )
71-
123+
72124 if args .outdir != "." :
73125 logger .info (f"Output directory: { args .outdir } /" )
74126 logger .info (f"Log file: { log_path } " )
75-
127+
76128 except OSError as e :
77129 logger .error (f"Error creating directory { args .outdir } : { e } " )
78130 sys .exit (1 )
@@ -84,26 +136,30 @@ def main():
84136 sys .exit (1 )
85137
86138 total_tracks = len (samples )
87- if args .gc_skew : total_tracks += 1
88- total_tracks += 1 # Genes
89- if args .annotations : total_tracks += 1
90-
139+ if args .gc_skew :
140+ total_tracks += 1
141+ total_tracks += 1 # Genes
142+ if args .annotations :
143+ total_tracks += 1
144+
91145 # 3. Init Ring
92146 ring = GenomeRing (
93- args .reference ,
147+ args .reference ,
94148 outdir = args .outdir ,
95149 track_width = args .track_width ,
96150 track_gap = args .track_gap ,
97151 palette = args .palette ,
98152 total_tracks = total_tracks ,
99153 no_backbone = args .no_backbone , # <--- New Arg
100- label_size = args .label_size # <--- New Arg
154+ label_size = args .label_size , # <--- New Arg
101155 )
102-
156+
103157 logger .info (f"Reference loaded. Mapping { len (samples )} samples..." )
104158 if args .min_identity > 0 or args .min_coverage > 0 :
105- logger .info (f" (Filters: Identity >= { args .min_identity } %, Coverage >= { args .min_coverage } %)" )
106-
159+ logger .info (
160+ f" (Filters: Identity >= { args .min_identity } %, Coverage >= { args .min_coverage } %)"
161+ )
162+
107163 # 4. Features
108164 if args .highlights :
109165 ring .add_highlights (args .highlights )
@@ -114,36 +170,45 @@ def main():
114170 # 5. Map & Draw
115171 for sample in samples :
116172 hits = map_sample (
117- ring .ref_path ,
118- sample ,
119- min_identity = min_id_decimal ,
120- min_coverage = min_cov_decimal
173+ ring .ref_path ,
174+ sample ,
175+ min_identity = min_id_decimal ,
176+ min_coverage = min_cov_decimal ,
121177 )
122-
123- is_reads = sample .seq_type .lower () in ["nanopore" , "illumina" , "illumina_paired_end" , "pacbio" , "hifi" ]
178+
179+ is_reads = sample .seq_type .lower () in [
180+ "nanopore" ,
181+ "illumina" ,
182+ "illumina_paired_end" ,
183+ "pacbio" ,
184+ "hifi" ,
185+ ]
124186 plot_mode = "coverage" if is_reads else "rect"
125-
187+
126188 ring .add_track (
127- sample .name ,
128- hits ,
129- plot_type = plot_mode ,
189+ sample .name ,
190+ hits ,
191+ plot_type = plot_mode ,
130192 min_identity = min_id_decimal ,
131193 min_coverage = min_cov_decimal ,
132- save_data = should_save
194+ save_data = should_save ,
133195 )
134196
135197 # 6. Genes (with filtering)
136- exclude_list = [x .strip () for x in args .exclude_genes .split (',' )] if args .exclude_genes else []
198+ exclude_list = (
199+ [x .strip () for x in args .exclude_genes .split ("," )] if args .exclude_genes else []
200+ )
137201 ring .add_genes_track (exclude_list = exclude_list )
138-
202+
139203 if args .annotations :
140204 ring .add_custom_track (args .annotations )
141205
142206 # 7. Save
143207 ring .save (args .output , dpi = args .dpi , title = args .title , no_legend = args .no_legend )
144-
208+
145209 full_output_path = os .path .join (args .outdir , args .output )
146210 logger .info (f"Done! Saved to { full_output_path } " )
147211
212+
148213if __name__ == "__main__" :
149- main ()
214+ main ()
0 commit comments