diff --git a/.replit b/.replit index 29231cf..8c115c8 100644 --- a/.replit +++ b/.replit @@ -1,4 +1,6 @@ -modules = ["bash", "python-3.11", "python-3.12"] + +modules = ["python-3.12", "bash"] + run = "~/workspace/assignments/grade_hw.sh" [nix] @@ -6,3 +8,15 @@ channel = "stable-24_05" [deployment] run = ["sh", "-c", "~/workspace/assignments/grade_hw.sh"] + +[workflows] +runButton = "Run" + +[[workflows.workflow]] +name = "Run" +author = 39028111 +mode = "sequential" + +[[workflows.workflow.tasks]] +task = "shell.exec" +args = "python main.py" diff --git a/assignments/01_howdy/howdy.py b/assignments/01_howdy/howdy.py new file mode 100755 index 0000000..8189145 --- /dev/null +++ b/assignments/01_howdy/howdy.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +""" +Author : Khaira Kukich +Date : 2025-03-24 +Purpose: Print greeting +""" + +import argparse +from enum import Flag + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Print greeting', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('-g', '--greeting', default= 'Howdy', metavar= 'str', help='The greeting') + + parser.add_argument('-n','--name', default= 'Stranger', metavar= 'str', help='Whom to greet') + + parser.add_argument('-e','--excited', help='Print ! if this flag is present', action='store_true') + return parser.parse_args() + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + greeting = args.greeting + name = args.name + excited = args.excited + + if excited: + print(f'{greeting}, {name}!') + else: + print(f'{greeting}, {name}.') + + + + +# -------------------------------------------------- +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/assignments/02_divide/divide.py b/assignments/02_divide/divide.py new file mode 100755 index 0000000..8f039e7 --- /dev/null +++ b/assignments/02_divide/divide.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +""" +Author : Khaira Kukich +Date : 2025-04-03 +Purpose: Dividing two numbers +""" + +import argparse + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + parser = argparse.ArgumentParser( + description='Divide two numbers', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('INT', metavar= 'INT', nargs= 2, type=valid_int, help= 'Numbers to divide') + return parser + +def valid_int(value): + try: + ivalue= int(value) + except ValueError: + raise argparse.ArgumentTypeError(f'invalid int value: {value}') + + return ivalue +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + parser = get_args() + args = parser.parse_args() + numerator, denominator = args.INT + + if args.INT[1] == 0: + parser.error('Cannot divide by zero, dum-dum!') + + result = numerator // denominator + print(f'{numerator} / {denominator} = {result}') + + +# -------------------------------------------------- +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/assignments/03_dna/dna.py b/assignments/03_dna/dna.py new file mode 100755 index 0000000..f2ce0b8 --- /dev/null +++ b/assignments/03_dna/dna.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +""" +Author : Khaira Kukich +Date : 2025-04-05 +Purpose: +""" + +import argparse + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + parser = argparse.ArgumentParser(description="Tetranucleotide frequency") + parser.add_argument("DNA", help="Input DNA sequence") + return parser.parse_args() + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + sequence = args.DNA.upper() + a_count = sequence.count('A') + c_count = sequence.count('C') + g_count = sequence.count('G') + t_count = sequence.count('T') + + print(f'{a_count} {c_count} {g_count} {t_count}') + + + + + +# -------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/assignments/04_revc/revc.py b/assignments/04_revc/revc.py new file mode 100755 index 0000000..aabf6f8 --- /dev/null +++ b/assignments/04_revc/revc.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +""" +Author : Khaira Kukich +Date : 2025-04-05 +Purpose: Print the Reverse complement of DNA +""" + +import argparse +import os + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Print the reverse complement of DNA', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('DNA', help="Input sequence or file") + + + return parser.parse_args() + +def read_sequence(dna_args): + if os.path.isfile(dna_args): + with open(dna_args) as file: + return file.read().strip() + else: + return dna_args.strip() + +def reverse_complement(sequence): + """Return the reverse complement of a DNA sequence""" + complement = {'A': 'T', 'T': 'A', 'C': 'G', 'G': 'C', 'a': 't', 't': 'a', 'c': 'g', 'g': 'c'} + return ''.join(complement.get(base, base) for base in reversed(sequence)) +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + sequence = read_sequence(args.DNA) + revc = reverse_complement(sequence) + print(revc) + + + +# -------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/assignments/05_gc/Rosalind_0808 b/assignments/05_gc/Rosalind_0808 new file mode 100644 index 0000000..e69de29 diff --git a/assignments/05_gc/Rosalind_5959 b/assignments/05_gc/Rosalind_5959 new file mode 100644 index 0000000..e69de29 diff --git a/assignments/05_gc/Rosalind_6404 b/assignments/05_gc/Rosalind_6404 new file mode 100644 index 0000000..e69de29 diff --git a/assignments/05_gc/cgc.py b/assignments/05_gc/cgc.py new file mode 100755 index 0000000..dffa25b --- /dev/null +++ b/assignments/05_gc/cgc.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +""" +Author : Khaira Kukich +Date : 2025-04-05 +Purpose: Compute GC content +""" + +import argparse +import os +import sys + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Compute GC content', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument("FILE", nargs="?", help='Input sequence file') + + return parser.parse_args() + +def read_fasta(file): + sequences = {} + current_id = None + for line in file: + line = line.strip() + if line.startswith('>'): + current_id = line[1:] + sequences[current_id] = '' + else: + sequences[current_id] += line + return sequences +def gc_content(sequence): + gc = sum(1 for base in sequence if base.upper() in "GC") + return (gc / len(sequence)) * 100 if sequence else 0.0 +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + if args.FILE: + if not os.path.isfile(args.FILE): + print('usage: cgc.py [-h] FILE') + print(f"cgc.py: error: No such file or directory: '{args.FILE}'") + sys.exit(1) + with open(args.FILE) as file: + sequences = read_fasta(file) + else: + sequences = read_fasta(sys.stdin) + max_id = None + max_gc = 0.0 + + for seq_id, sequence in sequences.items(): + gc = gc_content(sequence) + if gc > max_gc: + max_gc = gc + max_id = seq_id + print(f'{max_id} {max_gc:.6f}') + + + +# -------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/assignments/06_rna/rna.py b/assignments/06_rna/rna.py new file mode 100755 index 0000000..1a8decb --- /dev/null +++ b/assignments/06_rna/rna.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +""" +Author : Khaira Kukich +Date : 2025-04-05 +Purpose: Transcribe DNA into RNA +""" + +import argparse +import os +import sys +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Transcribe DNA into RNA') + parser.add_argument('files', metavar= 'FILE', nargs='+', help='Input DNA file', type=str) + parser.add_argument('-o', '--out_dir', help='Output directory (default: out', metavar='DIR', default='out') + + return parser.parse_args() + + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + out_dir = args.out_dir + files = args.files + + if not os.path.isdir(out_dir): + os.makedirs(out_dir) + + total_sequences = 0 + for file in files: + try: + basename = os.path.basename(file) + out_path = os.path.join(out_dir, basename) + with open(file) as fh_in, open(out_path, 'wt') as fh_out: + for line in fh_in: + rna = line.strip().replace("T", "U") + fh_out.write(rna + '\n') + total_sequences += 1 + except FileNotFoundError: + print(f"usage: {os.path.basename(sys.argv[0])} [-h]", file=sys.stderr) + print(f"No such file or directory: '{file}'", file=sys.stderr) + sys.exit(1) + + num_files = len(files) + seq_str = 'sequence' if total_sequences == 1 else 'sequences' + file_str = 'file' if num_files == 1 else 'files' + print(f'Done, wrote {total_sequences} {seq_str} in {num_files} {file_str} to directory "{out_dir}".') + + + +# -------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/assignments/07_syndna/syndna.py b/assignments/07_syndna/syndna.py new file mode 100755 index 0000000..5f74adb --- /dev/null +++ b/assignments/07_syndna/syndna.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +""" +Author : Khaira Kukich +Date : 2025-04-26 +Purpose: Create synthetic DNA/RNA sequences +""" + +import argparse +import random +import sys + + +def get_args(): + """Get command-line arguments""" + parser = argparse.ArgumentParser( + description='Create synthetic sequences', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('-o', + '--outfile', + type=argparse.FileType('wt'), + default='out.fa', + help='Output filename') + + parser.add_argument('-t', + '--seqtype', + metavar='str', + type=str.lower, + choices=['dna', 'rna'], + default='dna', + help='DNA or RNA') + + parser.add_argument('-n', + '--numseqs', + metavar='int', + type=int, + default=10, + help='Number of sequences to create') + + parser.add_argument('-m', + '--minlen', + metavar='int', + type=int, + default=50, + help='Minimum length') + + parser.add_argument('-x', + '--maxlen', + metavar='int', + type=int, + default=75, + help='Maximum length') + + parser.add_argument('-p', + '--pctgc', + metavar='float', + type=float, + default=0.5, + help='Percent GC') + + parser.add_argument('-s', + '--seed', + metavar='int', + type=int, + default=None, + help='Random seed') + + args = parser.parse_args() + + + if not 0 < args.pctgc < 1: + parser.error(f'--pctgc "{args.pctgc}" must be between 0 and 1') + + + if args.minlen < 1: + parser.error('--minlen must be > 0') + if args.maxlen < args.minlen: + parser.error('--maxlen must be >= --minlen') + + return args + + +def create_pool(pctgc, max_len, seq_type): + """Create the pool of bases""" + t_or_u = 'T' if seq_type == 'dna' else 'U' + num_gc = int((pctgc / 2) * max_len) + num_at = int(((1 - pctgc) / 2) * max_len) + pool = 'A' * num_at + 'C' * num_gc + 'G' * num_gc + t_or_u * num_at + + + for _ in range(max_len - len(pool)): + pool += random.choice(pool) + + return ''.join(sorted(pool)) + + +def main(): + """Main program""" + args = get_args() + random.seed(args.seed) + pool = create_pool(args.pctgc, args.maxlen, args.seqtype) + + for i in range(1, args.numseqs + 1): + seq_len = random.randint(args.minlen, args.maxlen) + seq = ''.join(random.sample(pool, seq_len)) + args.outfile.write(f'>{i}\n{seq}\n') + + args.outfile.close() + + + plural_seq = 'sequence' if args.numseqs == 1 else 'sequences' + print(f'Done, wrote {args.numseqs} {args.seqtype.upper()} {plural_seq} to "{args.outfile.name}".') + + +# -------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/assignments/08_common/common.py b/assignments/08_common/common.py new file mode 100755 index 0000000..f532df0 --- /dev/null +++ b/assignments/08_common/common.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +""" +Author : Khaira Kukich +Date : 2025-05-06 +Purpose: Find common words between two files +""" + +import argparse +import sys + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Find common words', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('file1', + metavar='FILE1', type=argparse.FileType('rt'), + help='Input file 1') + + parser.add_argument('file2', + help='Input file 2', + metavar='FILE2', + type=argparse.FileType('rt')) + + parser.add_argument('-o', + '--outfile', + help='Output file (default: stdout)', + metavar='FILE', + type=argparse.FileType('wt'), + default=sys.stdout) + + return parser.parse_args() +def get_words(filehandle): + words = set() + for line in filehandle: + for word in line.split(): + words.add(word) + return words + +# -------------------------------------------------- +def main(): + """Make a jazz noise here""" + + args = get_args() + + words1 = get_words(args.file1) + words2 = get_words(args.file2) + + common = sorted(words1 & words2) + + for word in common: + print(word, file=args.outfile) + + +# -------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/assignments/09_blastomatic/blastomatic.py b/assignments/09_blastomatic/blastomatic.py new file mode 100755 index 0000000..80c558d --- /dev/null +++ b/assignments/09_blastomatic/blastomatic.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +""" +Author : Khaira Kukich +Date : 2025-05-07 +Purpose: Annotate BLAST output +""" + +import argparse +import csv +import sys + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + parser = argparse.ArgumentParser( + description='Annotate BLAST output', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('-b', '--blasthits', + metavar='str', type=argparse.FileType('rt'), required=True, + help='BLAST -outfmt 6') + + parser.add_argument('-a', '--annotations', metavar='str', + help='Annotations file', + type=argparse.FileType('rt'), + required=True) + + parser.add_argument('-o', '--outfile', + help='Output file', + type=argparse.FileType('wt'), + default='out.csv', metavar='str') + + parser.add_argument('-d', '--delimiter', + help='Output field delimiter', + default=',', + metavar='str') + + parser.add_argument('-p', '--pctid', + help='Minimum percent identity', + type=float, metavar='float', + default=0.0) + return parser.parse_args() + + +# -------------------------------------------------- +def guess_delimiter(filename): + with open(filename, 'rt', newline='') as f: + sample = f.read(1024) + sniffer = csv.Sniffer() + return sniffer.sniff(sample).delimiter + + +# -------------------------------------------------- +def main(): + args = get_args() + + try: + ann_delim = guess_delimiter(args.annotations.name) + print(f'Guessed delimiter for annotations file: {ann_delim}') + reader = csv.DictReader(args.annotations, delimiter=ann_delim) + + annotations = {} + for row in reader: + qseqid = row.get('qseqid') + if qseqid: + annotations[qseqid] = row + print(f'Annotations (first 3 rows): {list(annotations.items())[:3]}') + out_fields = ['qseqid', 'pident', 'depth', 'lat_lon'] + writer = csv.DictWriter(args.outfile, fieldnames=out_fields, delimiter=args.delimiter) + writer.writeheader() + + reader = csv.reader(args.blasthits, delimiter='\t') + count = 0 + for row in reader: + if len(row) < 3: + continue + + qseqid = row[0] + try: + pident = float(row[2]) + except ValueError: + continue + print(f'Processing BLAST hit: {qseqid}, pident: {pident}') + if qseqid not in annotations: + print(f'DEBUG: {qseqid} not found in annotations') + elif pident >= args.pctid: + ann = annotations[qseqid] + writer.writerow({ + 'qseqid': qseqid, + 'pident': f'{pident:.3f}', + 'depth': ann.get('depth', ''), + 'lat_lon': ann.get('lat_lon', '') + }) + count += 1 + + print(f'Exported {count} hits to "{args.outfile.name}".') + + except Exception as e: + print(f'Error: {e}', file=sys.stderr) + sys.exit(1) + + + +# -------------------------------------------------- +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/assignments/10_conserved/conserved.py b/assignments/10_conserved/conserved.py new file mode 100755 index 0000000..ef8eeb0 --- /dev/null +++ b/assignments/10_conserved/conserved.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +""" +Author : Khaira Kukich +Date : 2025-05-07 +Purpose: Find Conserved Bases +""" + +import argparse + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser(description='Find conserved bases') + parser.add_argument('file', metavar='FILE', type=argparse.FileType('r'), + help='Input file with sequences') + return parser.parse_args() + +def read_sequences(file): + """Read sequences from the file and return them as a list""" + sequences = [] + for line in file: + line = line.strip() + if not line.startswith('>'): + sequences.append(line) + return sequences + +def find_conserved_bases(sequences): + """Find conserved bases in the sequences""" + + if len(set(len(seq) for seq in sequences)) > 1: + raise ValueError("Sequences have different lengths") + + conserved = [] + for i in range(len(sequences[0])): + bases = [seq[i] for seq in sequences] + if len(set(bases)) == 1: + conserved.append('|') + else: + conserved.append('X') + return ''.join(conserved) + +def main(): + args = get_args() + + try: + + sequences = read_sequences(args.file) + + + for seq in sequences: + print(seq) + + + conserved_bases = find_conserved_bases(sequences) + print(conserved_bases) + + except Exception as e: + print(f"Error: {e}") + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/assignments/11_run_length/run.py b/assignments/11_run_length/run.py new file mode 100755 index 0000000..4a152d7 --- /dev/null +++ b/assignments/11_run_length/run.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +""" +Author : Khaira Kukich +Date : 2025-05-07 +Purpose: Run Length Encoding +""" +#doooone +import argparse +import os + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Run-length encoding/data compression') + parser.add_argument('text', + metavar='str', + help='DNA text or file') + return parser.parse_args() + + +def rle(seq): + """Return run-length encoded string""" + if not seq: + return '' + + result = [] + count = 1 + prev = seq[0] + + for base in seq[1:]: + if base == prev: + count += 1 + else: + result.append(prev + (str(count) if count > 1 else '')) + prev = base + count = 1 + result.append(prev + (str(count) if count > 1 else '')) + return ''.join(result) + + +def main(): + args = get_args() + + if os.path.isfile(args.text): + with open(args.text) as fh: + for line in fh: + line = line.strip() + if line: + print(rle(line)) + else: + print(rle(args.text)) + + +def test_rle(): + """Test rle""" + assert rle('') == '' + assert rle('A') == 'A' + assert rle('ACGT') == 'ACGT' + assert rle('AA') == 'A2' + assert rle('AAAAA') == 'A5' + assert rle('ACCGGGTTTT') == 'AC2G3T4' + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/assignments/12_seqmagique/assert b/assignments/12_seqmagique/assert new file mode 100644 index 0000000..e69de29 diff --git a/assignments/12_seqmagique/seqmagique.py b/assignments/12_seqmagique/seqmagique.py new file mode 100755 index 0000000..191db5d --- /dev/null +++ b/assignments/12_seqmagique/seqmagique.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +""" +Author : Khaira Kukich +Date : 2025-05-07 +Purpose: Summarize FASTA file +""" + +import argparse +import os +import sys +from tabulate import tabulate +from typing import List + +#This one is a doozy, yikes, I'll take the 50% +# -------------------------------------------------- +def get_args(): + """Parse command-line arguments""" + parser = argparse.ArgumentParser( + description="Argparse Python script", + ) + parser.add_argument( + "files", + metavar="FILE", + type=str, + nargs="+", + help="Input FASTA file(s)", + ) + parser.add_argument( + "-t", + "--tablefmt", + metavar="table", + type=str, + default="plain", + help="Tabulate table style (default: plain)", + ) + return parser.parse_args() + + +def die(msg: str) -> None: + """Print error message and exit""" + print(f"usage: {sys.argv[0]} [-h] [-t table] FILE [FILE ...]", file=sys.stderr) + print(msg, file=sys.stderr) + sys.exit(1) + + +def parse_fasta(filename: str) -> List[str]: + """Parse FASTA file and return list of sequences""" + if not os.path.isfile(filename): + die(f"No such file or directory: \'{filename}\'") + + sequences = [] + seq = [] + + with open(filename, "rt") as file: + for line in file: + line = line.strip() + if not line: + continue + if line.startswith(">"): + if seq: + sequences.append("".join(seq)) + seq = [] + else: + seq.append(line) + + if seq: + sequences.append("".join(seq)) + + return sequences + + +def main(): + """Main function""" + args = get_args() + headers = ["name", "min_len", "max_len", "avg_len", "num_seqs"] + rows = [] + + for file in args.files: + try: + seqs = parse_fasta(file) + lengths = list(map(len, seqs)) + num_seqs = len(seqs) + min_len = min(lengths) if lengths else 0 + max_len = max(lengths) if lengths else 0 + avg_len = sum(lengths) / num_seqs if num_seqs else 0 + + avg_len = str(int(avg_len)) if avg_len == 0 else f"{avg_len:.2f}" + rows.append([file, min_len, max_len, avg_len, num_seqs]) + except SystemExit: + sys.exit(1) + + if not rows: + sys.exit(1) + + print(f"{'name':<26} {'min_len':<8} {'max_len':<8} {'avg_len':<8} {'num_seqs':<8}") + for row in rows: + name, min_len, max_len, avg_len, num_seqs = row + avg_len = f"{avg_len:.2f}" if avg_len != 0 else '0' + num_seqs = str(num_seqs) + print(f"{name:<26} {min_len:<8} {max_len:<8} {avg_len:<8} {num_seqs:<8}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/bin/new.py b/bin/new.py index 98f05b7..a696220 100755 --- a/bin/new.py +++ b/bin/new.py @@ -191,4 +191,4 @@ def get_defaults(): # -------------------------------------------------- if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/exercises/01_hello/hello.py b/exercises/01_hello/hello.py new file mode 100755 index 0000000..364af48 --- /dev/null +++ b/exercises/01_hello/hello.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# Purpose: Say Hello +import argparse + + +def get_args(): + parser = argparse.ArgumentParser(description='Say hello') + parser.add_argument('-n', '--name', metavar='name', + default='World', help='Name to greet') + + return parser.parse_args() + + +def main(): + args = get_args() +print('Hello, ' + args.name + '!') + +if __name__ == '__main__': + main() + diff --git a/exercises/02_crowsnest/crowsnest.py b/exercises/02_crowsnest/crowsnest.py new file mode 100755 index 0000000..fa30671 --- /dev/null +++ b/exercises/02_crowsnest/crowsnest.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +""" +Author : Add your Name +Date : 2025-04-01 +Purpose: Crow's Nest -- choose the correct article +""" + +import argparse + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Crows Nest -- choose the correct article', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('word', + metavar='word', + help= 'A word') + + + + return parser.parse_args() + + +# -------------------------------------------------- +def main(): + args = get_args() + word = args.word + + + + if word[0].lower() in 'aeiou': article = 'an' + else: article = 'a' + print(f'Ahoy, Captain, {article} {word} off the larboard bow!') +# ----------------------------------------------- +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/exercises/03_picnic/picnic.py b/exercises/03_picnic/picnic.py new file mode 100755 index 0000000..b6890f8 --- /dev/null +++ b/exercises/03_picnic/picnic.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +""" +Author : Add your Name +Date : 2025-04-03 +Purpose: Add Your Purpose +""" + +import argparse + + +# -------------------------------------------------- +def get_args(): + """Get command-line arguments""" + + parser = argparse.ArgumentParser( + description='Picnic game', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('Item', metavar= 'str', nargs='+', help='Item(s) to bring') + parser.add_argument('-s', '--sorted', action='store_true', help='Sort the items') + + + + return parser.parse_args() + + +# -------------------------------------------------- +def main(): + args = get_args() + items = args.Item + print(f'You are bringing {items}.') + + +# -------------------------------------------------- +if __name__ == '__main__': + main() diff --git a/extra_practice/09_csvfilter/csvfilter.py b/extra_practice/09_csvfilter/csvfilter.py new file mode 100755 index 0000000..cf1a226 --- /dev/null +++ b/extra_practice/09_csvfilter/csvfilter.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +""" +Author : Add your Name +Date : 2025-05-07 +Purpose: Add Your Purpose +""" + +import argparse +import csv +import re +import sys +def get_args(): + """Get command-line arguments""" + parser = argparse.ArgumentParser(description='Filter delimited records') + parser.add_argument('-f', '--file', type=argparse.FileType('r'), required=True, help='Input file') + parser.add_argument('-v', '--val', required=True, help='Value for filter') + parser.add_argument('-c', '--col', help='Column name for filter') + parser.add_argument('-o', '--outfile', type=str, default='out.csv', help='Output file name') + parser.add_argument('-d', '--delimiter', default=',', help='Input file delimiter (default: ",")') + return parser.parse_args() + +def main(): + args = get_args() + + reader = csv.DictReader(args.file, delimiter=args.delimiter) + fieldnames = reader.fieldnames + + if args.col and args.col not in fieldnames: + print(f'Error: --col "{args.col}" not a valid column!', file=sys.stderr) + sys.exit(1) + else: + writer = csv.DictWriter(open(args.outfile, 'w', newline=''), fieldnames=fieldnames, delimiter=args.delimiter) + writer.writeheader() + + count = 0 + + for row in reader: + if args.col: + text = row[args.col] + else: + text = " ".join(row.values()) + if re.search(args.val, text, re.IGNORECASE): + writer.writerow(row) + count += 1 + print(f'Done, wrote {count} to "{args.outfile}".') +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..32090c4 --- /dev/null +++ b/main.py @@ -0,0 +1,6 @@ + +def main(): + print("Hello from Python!") + +if __name__ == "__main__": + main() diff --git a/project/01_caesar/caesar.py b/project/01_caesar/caesar.py new file mode 100755 index 0000000..037991d --- /dev/null +++ b/project/01_caesar/caesar.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +""" +Author : Khaira Kukich +Date : 2025-05-07 +Purpose: Implement Caesar Shift +""" + +import argparse +import sys +import os + +def build_substitution_table(shift, decode=False): + alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + shifted = alpha[shift:] + alpha[:shift] + if decode: + return dict(zip(shifted, alpha)) + else: + return dict(zip(alpha, shifted)) + +def caesar_shift(text, table): + result = [] + for char in text: + upper_char = char.upper() + if upper_char in table: + result.append(table[upper_char]) + else: + result.append(char) + return ''.join(result) + +def valid_file(filename): + if not os.path.isfile(filename): + raise argparse.ArgumentTypeError( + f"No such file or directory: '{filename}'") + return filename + +def get_args(): + parser = argparse.ArgumentParser( + description="caesar shift", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + + parser.add_argument('FILE', + metavar='FILE', + help='Input file', + type=valid_file) + + parser.add_argument('-n', + '--number', + help='A number to shift', + type=int, + default=3) + + parser.add_argument('-d', + '--decode', + help='Decode the message', + action='store_true') + + parser.add_argument('-o', + '--outfile', + help='Output file (default: stdout)', + type=argparse.FileType('wt'), + default=sys.stdout) + + return parser.parse_args() + +def main(): + args = get_args() + shift = args.number % 26 + sub_table = build_substitution_table(shift, args.decode) + + with open(args.FILE, 'r') as infile: + for line in infile: + transformed = caesar_shift(line.rstrip(), sub_table) + print(transformed.upper(), file=args.outfile) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/testing.py b/testing.py index 2274627..774ce07 100755 --- a/testing.py +++ b/testing.py @@ -1,8 +1,13 @@ #!/usr/bin/env python3 """ Author : Add your Name + +Date : 2025-01-22 +Purpose: This is a test script +======= Date : 2025-01-24 Purpose: Add Your Purpose + """ import argparse @@ -13,7 +18,11 @@ def get_args(): """Get command-line arguments""" parser = argparse.ArgumentParser( + + description='This is a test script', +======= description='Add Your Purpose', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument('positional',