From e12492620b0b32354075da6faa3bbe5401670ece Mon Sep 17 00:00:00 2001 From: Leam Hall Date: Thu, 14 Dec 2023 06:15:20 -0600 Subject: [PATCH] Fixes issue 9, csv.DictReader --- Makefile | 19 ++++ data/jobs.txt | 7 +- data/pocs.txt | 5 +- job_seeker.py | 195 ++++++++++++++++++++++++---------------- test/test_job_seeker.py | 147 +++++++++++++++--------------- 5 files changed, 217 insertions(+), 156 deletions(-) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..57ec145 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +# Makefile + +SHELL = /usr/bin/bash + +.PHONY:test +test: + python -m unittest + +clean: + find . -type f -name "*.pyc" -exec rm {} \; + find . -type f -name "*.swp" -exec rm {} \; + +all: clean test + coverage run -m unittest + coverage report -m + python -m black -l79 . + -flake8 --ignore E251,E266,W391 + + diff --git a/data/jobs.txt b/data/jobs.txt index 91633c2..3a0207a 100644 --- a/data/jobs.txt +++ b/data/jobs.txt @@ -1,8 +1,3 @@ -# data/jobs.txt - - - +first_contact;last_contact;poc_name;company;notes;active;url;title 20230123;20230201;Fred Smythe; Some Great Place, LLC; linux, ansible, python; y; https://example.com/r12345; Senior Automation Engineer - 20230101; 20230101; Jason Jayson; Can't read resume, Inc; Windows server, powershell; n; https://whocares.com; Winderz admin - diff --git a/data/pocs.txt b/data/pocs.txt index 020b214..e700fa2 100644 --- a/data/pocs.txt +++ b/data/pocs.txt @@ -1,7 +1,4 @@ -# data/pocs.txt +name;phone;email;first_contact;last_contact Fred Smythe; 555.555.1212; fred@example.com; Example, Inc ; 20230123; 20202102 Jayne Johnson; 123.456.7890; jj@otherexample.com; Other Recruiter, LLC; 20221212; 20221212 - - Jason Jayson; br-549; jay@whocares.com; Whocares, Inc; 20230101; 20230101 - diff --git a/job_seeker.py b/job_seeker.py index 52e4de4..0c05aac 100755 --- a/job_seeker.py +++ b/job_seeker.py @@ -6,25 +6,42 @@ # desc: Track data on job applications import argparse +import csv from datetime import datetime as dt import os.path import sys class Job: - """ Stores the job req data """ - def __init__(self, job_data = {}): - self.title = job_data.get("title", None) - self.active = job_data.get("active", "y") - self.notes = job_data.get("notes", None) - self.company = job_data.get("company", None) - self.url = job_data.get("url", None) - self.poc_name = job_data.get("poc_name", None) - self.last_contact = job_data.get("last_contact", convert_date(dt.now())) - self.first_contact = job_data.get("first_contact", convert_date(dt.now())) + """Stores the job req data""" + + def __init__(self, job_data={}): + self.title = job_data.get("title", "") + self.active = job_data.get("active", "y") + self.notes = job_data.get("notes", "") + self.company = job_data.get("company", "") + self.url = job_data.get("url", "") + self.poc_name = job_data.get("poc_name", "") + self.last_contact = job_data.get( + "last_contact", convert_date(dt.now()) + ) + self.first_contact = job_data.get( + "first_contact", convert_date(dt.now()) + ) + self.searchables = [ + self.title.lower(), + self.notes.lower(), + self.company.lower(), + self.url.lower(), + self.poc_name.lower(), + ] def __str__(self): - return "{}\nActive: {} Notes: {}\n{}, {}\n{} \nLast chat: {}, First chat: {}".format( + if self.active == "y": + self.active = "Yes" + else: + self.active = "No" + return "Title: {}\nActive: {}\nNotes: {}\nCompany: {} ({})\nPOC: {} \nLast contact: {}\nFirst contact: {}".format( self.title, self.active, self.notes, @@ -35,70 +52,82 @@ def __str__(self): self.first_contact, ) + def job_builder(line): _list = string_to_list(line) data = { - "last_contact": _list[0], - "first_contact": _list[1], - "poc_name": _list[2], - "company": _list[3], - "notes": _list[4], - "active": _list[5], - "url": _list[6], - "title": _list[7], + "last_contact": _list[0], + "first_contact": _list[1], + "poc_name": _list[2], + "company": _list[3], + "notes": _list[4], + "active": _list[5], + "url": _list[6], + "title": _list[7], } - return Job(data) + return Job(data) -class POC(): - """ Stores the contact info for each Point of Contact """ - def __init__(self, data = {}): - self.name = data.get('name', None) - self.company = data.get('company', None) - self.phone = data.get('phone', None) - self.email = data.get('email', None) - self.first_contact = data.get("first_contact", convert_date(dt.now())) - self.last_contact = data.get('last_contact', convert_date(dt.now())) +class POC: + """Stores the contact info for each Point of Contact""" + + def __init__(self, data={}): + self.name = data.get("name", "") + self.company = data.get("company", "") + self.phone = data.get("phone", "") + self.email = data.get("email", "") + self.first_contact = data.get("first_contact", convert_date(dt.now())) + self.last_contact = data.get("last_contact", convert_date(dt.now())) + self.searchables = [ + self.name.lower(), + self.company.lower(), + self.email.lower(), + ] def __str__(self): - """ Returns a formatted string with the POC info """ + """Returns a formatted string with the POC info""" return "{}, ({}) {} [{}]\nFirst Contact: {}, Last Contact: {}".format( - self.name, + self.name, self.phone, self.email, self.company, self.first_contact, - self.last_contact) + self.last_contact, + ) + def poc_builder(line): _list = string_to_list(line) data = { - "name": _list[0], - "phone": _list[1], - "email": _list[2], - "company": _list[3], - "first_contact": _list[4], - "last_contact": _list[5], - } + "name": _list[0], + "phone": _list[1], + "email": _list[2], + "company": _list[3], + "first_contact": _list[4], + "last_contact": _list[5], + } return POC(data) - + + def convert_date(date): - """ Takes a datetime.datetime object and returns a YYYMMDD string """ + """Takes a datetime.datetime object and returns a YYYMMDD string""" return "{}{:0>2}{:0>2}".format(date.year, date.month, date.day) + def list_from_file(filename): - """ Takes a file, removes comments/empty lines, returns a list of each line """ + """Takes a file, removes comments/empty lines, returns a list of each line""" lines = [] - with open(filename, 'r') as f: + with open(filename, "r") as f: for line in f: line = line.strip() if len(line) > 5 and not line.startswith("#"): lines.append(line) return lines + def parse_list(_list, _list_type, search): - """ Takes a list, and the element type, and prints any that match search """ - items = [] + """Takes a list, and the element type, and prints any that match search""" + items = [] for element in _list: if search.lower() in element.lower(): if _list_type == "poc": @@ -106,52 +135,66 @@ def parse_list(_list, _list_type, search): if _list_type == "job": items.append(job_builder(element)) return items - -def string_to_list(data, sep = ';'): - """ Takes a sep separated string and converts it to a list """ - return [ e.strip() for e in data.split(sep) ] - + + +def string_to_list(data, sep=";"): + """Takes a sep separated string and converts it to a list""" + return [e.strip() for e in data.split(sep)] + + +def items_from_file(filename, klass): + """Takes a filename, and returns objects based on that file.""" + with open(filename, "r") as f: + reader = csv.DictReader(f, delimiter=";") + result = [klass(row) for row in reader] + + return result + + +def search_items(search_term, *lists): + """ + Searches the values of each item in a list of objects. + Returns a list of objects with the search_term. + """ + results = [] + for _list in lists: + for item in _list: + for searchable in item.searchables: + if search_term.lower() in searchable: + results.append(item) + break + return results if __name__ == "__main__": + datadir = "data" + job_file = "jobs.txt" + poc_file = "pocs.txt" - datadir = "data" - job_file = "jobs.txt" - poc_file = "pocs.txt" try: - poc_list = list_from_file(os.path.join(datadir, poc_file)) - job_list = list_from_file(os.path.join(datadir, job_file)) + poc_list = items_from_file(os.path.join(datadir, poc_file), POC) + job_list = items_from_file(os.path.join(datadir, job_file), Job) except: print("Can't find the data files") sys.exit(1) parser = argparse.ArgumentParser() parser.add_argument( - "-a", "--add", - help = "add data, requires -r or -p", - action = "store_true") - parser.add_argument( - "-j", "--job", - help = "use the Job info", - action = "store_true") + "-a", "--add", help="add data, requires -r or -p", action="store_true" + ) parser.add_argument( - "-p", "--poc", - help = "use the POC info", - action = "store_true") + "-j", "--job", help="use the Job info", action="store_true" + ) parser.add_argument( - "-s", "--search", - help = "SEARCH for", - default = "") + "-p", "--poc", help="use the POC info", action="store_true" + ) + parser.add_argument("-s", "--search", help="SEARCH for", default="") args = parser.parse_args() if args.add: print("I am to add") - if args.job: - for job in parse_list(job_list, "job", args.search): - print(job, "\n") - elif args.poc: - for poc in parse_list(poc_list, "poc", args.search): - print(poc, "\n") - - + if args.search: + results = search_items(args.search, job_list, poc_list) + for result in results: + print(result, "\n") diff --git a/test/test_job_seeker.py b/test/test_job_seeker.py index 6a8a051..d2c570d 100755 --- a/test/test_job_seeker.py +++ b/test/test_job_seeker.py @@ -13,87 +13,85 @@ class TestJobSeeker(unittest.TestCase): - def setUp(self): self.job_data_1 = { - "last_contact": "20230123", - "first_contact": "20230201", - "poc_name": "Fred Smythe", - "company": "Some Great Place, LLC", - "notes": "linux, ansible, python", - "active": "y", - "url": "https://example.com/r12345", - "title": "Senior Automation Engineer", + "last_contact": "20230123", + "first_contact": "20230201", + "poc_name": "Fred Smythe", + "company": "Some Great Place, LLC", + "notes": "linux, ansible, python", + "active": "y", + "url": "https://example.com/r12345", + "title": "Senior Automation Engineer", } self.poc_data_1 = { - "name": "Fred Smythe", - "phone": "555.555.1212", - "email": "fred@example.com", - "company": "Example, Inc", - "first_contact": "20230123", - "last_contact": "20230201", + "name": "Fred Smythe", + "phone": "555.555.1212", + "email": "fred@example.com", + "company": "Example, Inc", + "first_contact": "20230123", + "last_contact": "20230201", } self.job_data_2 = { - "last_contact": "20230123", - "first_contact": "20230201", - "poc_name": "Jason Jayson", - "company": "Can't read resume, Inc", - "notes": "windows server, powershell", - "active": "n", - "url": "https://whocares.com", - "title": "Winderz admin", + "last_contact": "20230123", + "first_contact": "20230201", + "poc_name": "Jason Jayson", + "company": "Can't read resume, Inc", + "notes": "windows server, powershell", + "active": "n", + "url": "https://whocares.com", + "title": "Winderz admin", } self.poc_data_2 = { - "name": "Jason Jayson", - "phone": "br-549", - "email": "jay@whocares.com", - "company": "Whocares, Inc", - "first_contact": "20230101", - "last_contact": "20230101", + "name": "Jason Jayson", + "phone": "br-549", + "email": "jay@whocares.com", + "company": "Whocares, Inc", + "first_contact": "20230101", + "last_contact": "20230101", } - self.test_dir = tempfile.TemporaryDirectory() - self.data_file = os.path.join(self.test_dir.name, "data.txt") - with open(self.data_file, 'w') as f: + self.test_dir = tempfile.TemporaryDirectory() + self.data_file = os.path.join(self.test_dir.name, "data.txt") + with open(self.data_file, "w") as f: f.write("\n\n\n#bogus line\ngood line\n\n\n") def tearDown(self): self.test_dir.cleanup() - def test_convert_date(self): - date = dt.now() - expected = str(date.year) - result = job_seeker.convert_date(dt.now()) + date = dt.now() + expected = str(date.year) + result = job_seeker.convert_date(dt.now()) self.assertTrue(result.startswith(expected)) def test_string_to_list(self): - my_str = "linux; python; ansible " - expected = ["linux", "python", "ansible"] - result = job_seeker.string_to_list(my_str) + my_str = "linux; python; ansible " + expected = ["linux", "python", "ansible"] + result = job_seeker.string_to_list(my_str) self.assertTrue(result == expected) def test_job_defaults(self): - j = job_seeker.Job() - date = dt.now() - year = str(date.year) + j = job_seeker.Job() + date = dt.now() + year = str(date.year) self.assertTrue(j.last_contact.startswith(year)) self.assertTrue(j.first_contact.startswith(year)) self.assertTrue(j.active == "y") def test_job_data(self): j = job_seeker.Job(self.job_data_1) - self.assertTrue(j.last_contact == "20230123") + self.assertTrue(j.last_contact == "20230123") self.assertTrue(j.first_contact == "20230201") - self.assertTrue(j.poc_name == "Fred Smythe") - self.assertTrue(j.company == "Some Great Place, LLC") - self.assertTrue(j.notes == "linux, ansible, python") - self.assertTrue(j.active == "y") - self.assertTrue(j.url == "https://example.com/r12345") - self.assertTrue(j.title == "Senior Automation Engineer") + self.assertTrue(j.poc_name == "Fred Smythe") + self.assertTrue(j.company == "Some Great Place, LLC") + self.assertTrue(j.notes == "linux, ansible, python") + self.assertTrue(j.active == "y") + self.assertTrue(j.url == "https://example.com/r12345") + self.assertTrue(j.title == "Senior Automation Engineer") def test_job_builder(self): job_line = "20230123;20230201;Fred Smythe; Some Great Place, LLC; linux, ansible, python; y; https://example.com/r12345; Senior Automation Engineer" @@ -101,36 +99,45 @@ def test_job_builder(self): self.assertTrue(job.title == "Senior Automation Engineer") def test_poc_defaults(self): - p = job_seeker.POC() - date = dt.now() - year = str(date.year) - self.assertTrue(p.name == None) - self.assertTrue(p.email == None) - self.assertTrue(p.phone == None) + p = job_seeker.POC() + date = dt.now() + year = str(date.year) + self.assertFalse(p.name) + self.assertFalse(p.email) + self.assertFalse(p.phone) self.assertTrue(p.first_contact.startswith(year)) self.assertTrue(p.last_contact.startswith(year)) - + def test_poc_data(self): - p = job_seeker.POC(self.poc_data_1) - self.assertTrue(p.name == "Fred Smythe") - self.assertTrue(p.phone == "555.555.1212") - self.assertTrue(p.email == "fred@example.com") - self.assertTrue(p.company == "Example, Inc") + p = job_seeker.POC(self.poc_data_1) + self.assertTrue(p.name == "Fred Smythe") + self.assertTrue(p.phone == "555.555.1212") + self.assertTrue(p.email == "fred@example.com") + self.assertTrue(p.company == "Example, Inc") self.assertTrue(p.first_contact == "20230123") - self.assertTrue(p.last_contact == "20230201") + self.assertTrue(p.last_contact == "20230201") def test_poc_string(self): - p = job_seeker.POC(self.poc_data_1) + p = job_seeker.POC(self.poc_data_1) results = p.__str__().split("\n") - self.assertTrue(results[0] == - "Fred Smythe, (555.555.1212) fred@example.com [Example, Inc]") - self.assertTrue(results[1] == - "First Contact: 20230123, Last Contact: 20230201") - + self.assertTrue( + results[0] + == "Fred Smythe, (555.555.1212) fred@example.com [Example, Inc]" + ) + self.assertTrue( + results[1] == "First Contact: 20230123, Last Contact: 20230201" + ) + def test_list_from_file(self): l = job_seeker.list_from_file(self.data_file) self.assertTrue(len(l) == 1) - self.assertTrue(l[0] == "good line") - + self.assertTrue(l[0] == "good line") + def test_parse_list(self): pass + + def test_searchables(self): + j = job_seeker.Job(self.job_data_1) + self.assertIn("fred smythe", j.searchables) + p = job_seeker.POC(self.poc_data_1) + self.assertIn("fred smythe", p.searchables)