Giter VIP home page Giter VIP logo

nicobrenner / commandjobs Goto Github PK

View Code? Open in Web Editor NEW
148.0 5.0 13.0 4.18 MB

Command Jobs uses AI to help software engineers find the best jobs

License: Apache License 2.0

Dockerfile 0.70% Shell 0.41% Python 98.89%
command-line-tool ncurses tui gpt gpt-3 gpt-4 jobsearch jobseeker jobseekers terminal terminal-based hacker-news hackernews hackernews-api hackernews-client hackernews-cli ycombinator-hacker-news command-line commandline

commandjobs's Introduction

     ██████╗ ██████╗ ███╗   ███╗███╗   ███╗ █████╗ ███╗   ██╗██████╗ 
    ██╔════╝██╔═══██╗████╗ ████║████╗ ████║██╔══██╗████╗  ██║██╔══██╗
    ██║     ██║   ██║██╔████╔██║██╔████╔██║███████║██╔██╗ ██║██║  ██║
    ██║     ██║   ██║██║╚██╔╝██║██║╚██╔╝██║██╔══██║██║╚██╗██║██║  ██║
    ╚██████╗╚██████╔╝██║ ╚═╝ ██║██║ ╚═╝ ██║██║  ██║██║ ╚████║██████╔╝
    ╚═════╝ ╚═════╝ ╚═╝     ╚═╝╚═╝     ╚═╝╚═╝  ╚═╝╚═╝  ╚═══╝╚═════╝ 
                                                                    
                       ██╗ ██████╗ ██████╗ ███████╗                 
                       ██║██╔═══██╗██╔══██╗██╔════╝                 
                       ██║██║   ██║██████╔╝███████╗                 
                  ██   ██║██║   ██║██╔══██╗╚════██║                 
                  ╚█████╔╝╚██████╔╝██████╔╝███████║                 
                   ╚════╝  ╚═════╝ ╚═════╝ ╚══════╝                 

📺 Use AI to find the best jobs for your resume and preferences

🧘🏻 A distraction-free, local-first, command line interface to scrape online jobs, and filter them to your needs

   

Using AI, Command Jobs makes sure to find only the absolute best matches for your experience, skills and job preferences

Stop wasting your time with online tools that are not built for you, the job finder

Command Jobs is the only job searching tool that runs from where you work, the terminal. And yes, it also doesn't make you read through hundreds of job listings just to find a couple of good matches

This is just starting out! Follow along as we improve it

To get started, check out Quick Start, Configuration and Usage

🙏🏼🤗❤️

Note: If you want to add another source of job listings, go to this issue and add it as a suggested source

Updates

  • Building in public:

    • ❤️ If you want to contribute to this project and want to take a crack at writing tests for it, it would be amazing! 🤗 Here's a ticket to write a new test, and a walk-through of the current test code: Request to create: Test displaying the resume text 🙏🏼

    • Video walkthrough, from git clone all the way to finding the best matches

      • Command Jobs Walkthrough
    • Here's a little bit of the internals of the application. Very high level overview of the features as well as the database. If you want to see more, or would like a deeper explanation, please create an Issue, thank you

      • Command Jobs Internals
    • Just wrote the first test! 😅 And it's in no small part thanks to Agentic's Glide, which they recently launched (see announcement here). I was about to switch from ncurses to python-prompt-toolkit, and failing that from python to Go, so I could build Command Jobs using Bubble Tea 🤩😍🤤

      • First test with Glide
    • Check out the amazing ancv, a tool for building a really cool ascii version of your resume on the terminal! 🤗 (love the joke with the Venn diagram). Will need to integrate it as a library with Command Jobs

    • Tried out ShellGPT and made a small PR to highlight its chat interface in the README. It's a pretty cool tool to use GPT from the terminal. Next I want to try coding a bit with aider

      • ShellGPT
    • Decided to try to build this project as openly as possible, in that spirit, I just recorded a coding session in which I go through the process of trying to resolve a bug (issue #12), and finding 3 other bugs instead!

      If you are just getting started with coding, it's also a pretty good overview of a basic software project management. In the video I show the whole workflow of not only writing code, but also managing an environment, dealing with errors, documenting the process in Github, managing git and branches, commiting, pushing and merging code, updating documentation (like now), and sharing/promoting

    • Trying to solve #12

  • Thank you to the Hacker News community for the encouragement, enthusiasm and support. Check out this thread: Show HN: Tech jobs on the command line

Features

  • View and navigate AI-matched job listings directly from the terminal "AI job matches"

  • Scrape job listings from "Ask HN: Who's hiring?" posts on Hacker News

    "Ask HN: Who's hiring?" March 2024

  • Process listings with GPT to find the best matches for you

    • The app asks GPT for each job listing, if it's a good fit for your resume
    • The prompt includes the resume, the job listing, a section for json formating the results, a role description, a job preferences section, and some additional questions
    • You get a filtered list of the best matches for your resume and preferences

In the works

  • Track job applications directly in the terminal

  • Scrape job listings from additional sources

  • Add cronjob that runs periodically to scrape

  • Alerts about new matches found

  • Anything you'd like to see? Please add a ticket

Usage

"Command Jobs main menu"

After going through the Configuration and successfully running Command Jobs

You will get a menu with the options below. To navigate the menu, just use the arrow keys and select options with Enter. You can quit at any time by pressing q

When first running the app, open the Edit Resume section and paste the text of your resume, no need to include your name or contact info (you can see an example resume on config/base_resume.sample. Alternatively, you can paste your resume text directly into a base_resume.txt file on the base folder of the code

Then, get some job listings into the app by running Scrape "Ask HN: Who's hiring?". You can see the first few listings in the Navigate jobs in the local db section (if you want to see more, you can also open job_listings.db directly with sqlite3 and check out the contents)

For the next step, make sure you've reviewed your .env file and have adapted the prompts to your preferences for job matching

Once you have your Resume ready, jobs in the local db and the prompts configured, run Find best matches for resume with AI. That will run through the listings to find a match of your resume and job preferences (for now, it is limited at 5 checks per run, you can modify that through changing the LIMIT in the query within fetch_job_listings() in src/database_manager.py)

When the GPT analysis is done, you get access to the AI found X listings match your resume option, where you can navigate the best matches found

The menu includes:

  • Edit Resume: Add or replace the text of your resume for AI matching
  • Scrape "Ask HN: Who's hiring?": Scrape job listings from Hacker News
  • Navigate jobs in the local db: Browse listings stored locally
  • Find best matches for resume with AI: Match listings to your resume using AI
  • AI found X listings match your resume: Review personalized job matches

To exit the application, press q

Quick Start

Video walkthrough, from git clone all the way to finding the best matches

  • Command Jobs Walkthrough

Below is the step by step

  • Clone the repository:

    • git clone https://github.com/nicobrenner/commandjobs.git
    • cd commandjobs
  • Run via Docker

    1. Build the Docker image:

      • docker-compose -f docker/docker-compose.yml build
    2. Run the Docker container (make sure you've setup your OpenAI API key in your .env file - see Configuration section below):

      • docker-compose -f docker/docker-compose.yml run app
  • (if you don't want to use Docker) Run with Python in a Virtual Environment

    1. Set up a Python virtual environment and activate it:

      • python3 -m venv venv
      • source venv/bin/activate
    2. Install the dependencies:

      • pip install -r config/requirements.txt
    3. Run the application (make sure you've setup your OpenAI API key in your .env file - see Configuration section below):

      • python src/menu.py

Configuration

  1. Create a .env file in the root directory of the project by copying the config/sample.env file, and adding your OpenAI API key:

    cp config/sample.env .env edit the .env file to add your OpenAI API key

    OPENAI_API_KEY=your_openai_api_key_here
    OPENAI_GPT_MODEL=gpt-3.5-turbo
    
    BASE_RESUME_PATH=base_resume.txt
    HN_START_URL=https://news.ycombinator.com/item?id=40563283&p=1
    
    ...
    

    Note: the above HN_START_URL is for April 2024

    Obtaining an OpenAI API Key

    If you don't have an OpenAI API key, follow these instructions to obtain one.

  2. Modify the prompt so that it matches your preferences. The prompt has 5 sections:

    • COMMANDJOBS_ROLE: list the roles that you are looking for

      COMMANDJOBS_ROLE=backend engineer, or fullstack engineer, or senior engineer, or senior tech lead, or engineering manager, or senior enginering manager, or founding engineer, or founding fullstack engineer, or something similar
      
    • COMMANDJOBS_IDEAL_JOB_QUESTIONS: explain what is a good fit for you

      COMMANDJOBS_IDEAL_JOB_QUESTIONS=and the company uses either Ruby, Rails, Ruby on Rails, or Python, the position doesn't require any knowledge or experience in any of the following: {job_requirement_exclusions}, the position is remote, it's for the US and the description matches the resume? (Yes or No), justify the Yes or No about the role being a good fit for the experience of the resume in one sentence.
      
    • COMMANDJOBS_EXCLUSIONS: list things to avoid (this takes some trial and error to get right, iterating with the matches you get each time)

      COMMANDJOBS_EXCLUSIONS=VMS (video management systems), computer vision systems, Java, C++, C#, Grails, ML, Machine Learning, PyTorch, training models
      
    • COMMANDJOBS_PROMPT: the prompt includes all the other elements as well as the questions that we want answers about from GPT

      COMMANDJOBS_PROMPT=Given the below job listing html, and resume text. Listing:\n{job_html}\n\nResume:\n{resume}\n\nPlease provide the following information about the listing: brief 2 sentence summary of the listing, company name, [list of available positions, with individual corresponding links if available], tech stack description, do they use rails? (Yes or No), do they use python? (Yes or No), are the positions remote (not hybrid, not onsite)? (Yes or No), are they hiring in the US? (Yes or No), how to apply to the job? (provide 1 sentence max description, include link or email address if necessary), Does the role prioritize candidates with a background in a specific industry sector (e.g., tech, finance, healthcare)?, does the job seem like a good fit for the resume (Only say Yes if the role is for {roles} {ideal_job_questions}\n\nProvide output in JSON format, use this example for reference, always with the same keys, but replace the values with the answers for the previous requests for information: \n{output_format}
      
    • COMMANDJOBS_OUTPUT_FORMAT: this specifies the output format for the prompt, including an example to follow - it's important that the structure and fields of the format matches the questions from the prompt

      COMMANDJOBS_OUTPUT_FORMAT="{\n \"small_summary\": \"Wine and Open Source developers for C-language systems programming\",\n \"company_name\": \"CodeWeavers\",\n \"available_positions\": [\n {\n \"position\": \"Wine and General Open Source Developers\",\n \"link\": \"https://www.codeweavers.com/about/jobs\"\n }\n ],\n \"tech_stack_description\": \"C-language systems programming\",\n \"use_rails\": \"No\",\n \"use_python\": \"No\",\n \"remote_positions\": \"Yes\",\n \"hiring_in_us\": \"Yes\",\n \"how_to_apply\": \"Apply through our website, here is the link: https://www.codeweavers.com/about/jobs\",\n \"back_ground_with_priority\": null,\n \"fit_for_resume\": \"No\",\n \"fit_justification\": \"The position is for Wine and Open Source developers, neither of which the resume has experience with. The job is remote in the US\"\n }"
      
  3. Modify the query with filters for matching jobs.

    In the file src/display_matching_table.py, the method __init__ has a variable (self.good_match_filters) with the following SQL conditions:

    json_valid(gi.answer) = 1
    AND json_extract(gi.answer, '$.fit_for_resume') = 'Yes'
    AND json_extract(gi.answer, '$.remote_positions') = 'Yes'
    AND json_extract(gi.answer, '$.hiring_in_us') <> 'No'

    These 3 conditions represent the default criteria for filtering AI-found matches. Below is the breakdown of the 3 default requirements for a good match:

    1. The AI determined the listing a good match for the resume and preferences

      AND json_extract(gi.answer, '$.fit_for_resume') = 'Yes'
    2. The role is, or can be, remote

      AND json_extract(gi.answer, '$.remote_positions') = 'Yes'
    3. The role is hiring in the US (the value can be either Yes or NULL or '', so the condition checks that the field '$.hiring_in_us' is not 'No')

      AND json_extract(gi.answer, '$.hiring_in_us') <> 'No'

    Note: the database is a sqlite3 database, so you can also just open it sqlite3 job_listings.db and then try out a query like the one below, and then experiment to see what you find. Regardless of filtering, all the answers and prompts should be stored in the gpt_interactions table (checkout the latest update video about the internals):

    SELECT COUNT(gi.job_id)
        FROM gpt_interactions gi
        JOIN job_listings jl ON gi.job_id = jl.id
    WHERE json_valid(gi.answer) = 1
        AND json_extract(gi.answer, '$.fit_for_resume') = 'Yes'
        AND json_extract(gi.answer, '$.remote_positions') = 'Yes'
        AND json_extract(gi.answer, '$.hiring_in_us') <> 'No'

    You should adjust that to your preferences and you can mix and match with the questions/answers you want to get from your prompt

  4. Increase the limit of listings to check per batch

    The option COMMANDJOBS_LISTINGS_PER_BATCH (which should be in your .env file, see sample.env) determines how many listings are processed each time the menu option "Find best matches with AI" is executed. If you are using the default of 10, it means that every time you run the option "Find best matches", Command Jobs will make 10 requests to gpt. Once you trust the app, I recommend setting the limit to 500, so that the app can process all scraped listings in one go

Contributing

Priority

  • ❤️ If you want to contribute to this project and want to take a crack at writing tests for it, it would be amazing! 🤗 Here's a ticket to write a new test, and a walk-through of the current test code: Request to create: Test displaying the resume text 🙏🏼

We welcome contributions, especially in improving scrapers and enhancing user experience. If you'd like to help, please file an issue or pull request on our GitHub repository

Here's an overview of some of the internals of the app

  • Command Jobs Internals

Issues

Encounter any issues? Please file them on the project's GitHub repo. We appreciate your feedback and contributions to making Command Jobs better!

License

This project is open-source and available under the Apache 2.0 License.

Related projects

  • ancv, get a fancy version of your resume in your terminal, very cool

commandjobs's People

Contributors

benquigley avatar christosgousis avatar giorgospapageorgiou avatar nicobrenner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

commandjobs's Issues

Display keyboard shortcuts when viewing job

It'd be great to have the available keyboard shortcuts while viewing jobs or any view really. For example in the screenshot below I already learned I can use the arrow keys and q to go back, but it doesn't say so anywhere
image

Feature request: multi-tenant version of Command Jobs accessible via terminal

Two ways of going about it:

  1. have separate instances of command jobs running simultaneously
  2. have command jobs support multiple users at the same time, or

We could start with 1, then go for 2 if necessary

The app can be served via SSH (I imagine):

  • when someone connects via ssh, the server spawns a new shell/command that only runs commandjobs, and saves some sort of unique id for the user (their public ssh key maybe?)
  • to be able to resume the session later, the server would need to keep track of unique user ids
  • each job_listings.db and base_resume.txt file would need to be stored, paired with their corresponding unique user id

Making it more windows accessible .

Trying to run the program from windows is very tricky, and I have been encountering many issues
here are some tips that helped me and could be added to the readme file:
1: " .\venv\Scripts\Activate " should be used instead of " source venv/bin/activate" in windows .
2. " pip install windows-curses " is needed to be installed through terminal
3. " pip install python-dotenv " is also needed to be installed

now the only issue is that even though I have curses and dotenv installed it isn't able to work with my python .i want to figure out how to fix that in this issue
ps:We need to make a different section in readme for windows startup

Add tests

  • For pasting text
  • For running the app the first time after cloning the repo (see eg. #41)
  • For displaying text (the resume)
  • For displaying results
  • For running GPT
  • For putting together the prompt
  • For scraping
  • For saving the jobs in the listings table

Adding instructions for vim file

Add a section to readme about how to paste your api key some points could be :
1.to edit the file you need to use delete cause backspace wont work
2.you need to press insert before editing or else it wont work
3.to save the file you need to type " :w " plus enter
4.to quit the file you need to type " :q " plus enter

Bug: app doesn't work when there's no resume (errors out)

There's a line that only runs when entering a new resume, and it should be a simple fix. The error is below

This is an important issue, as it might be preventing anyone new from using the app

Traceback (most recent call last):
  File "/repo/src/menu.py", line 380, in <module>
    curses.wrapper(main)
  File "/usr/local/lib/python3.9/curses/__init__.py", line 94, in wrapper
    return func(stdscr, *args, **kwds)
  File "/repo/src/menu.py", line 376, in main
    app = MenuApp(stdscr, logger)
  File "/repo/src/menu.py", line 75, in __init__
    self.setup()
  File "/repo/src/menu.py", line 109, in setup
    self.run()
  File "/repo/src/menu.py", line 166, in run
    self.handle_keypress(key)
  File "/repo/src/menu.py", line 174, in handle_keypress
    self.execute_menu_action()
  File "/repo/src/menu.py", line 199, in execute_menu_action
    self.manage_resume(self.stdscr)
  File "/repo/src/menu.py", line 264, in manage_resume
    input_lines = self.capture_text_with_scrolling(stdscr, "Enter/Paste your resume. Type 'END' to finish:")
TypeError: capture_text_with_scrolling() takes 2 positional arguments but 3 were given

Add additional sources (comment here to suggest another source)

  • workatastartup.com
  • public LinkedIn.com listings

========

Idea: create a p2p network of jobs - anyone in the network can post a job, the rest of the network gets the job listing and processes/matches it locally

This would allow that anyone that runs a scraper, can add listings to the network. How do we prevent it from getting flooded by a malicious actor (someone who wants to spam the network)? How do we curate the listings?

One way would be for the local nodes are responsible for filtering the things they receive. There could be filtering nodes as well, that individual nodes could subscribe to

What would be the incentive to post a job? Well all companies want to post job, so they would do it for free probably

What would be the incentive to filter the jobs? For the individual is easy, they don't want to get spammed with jobs

How about for the filtering nodes that want to act as a sort of aggregator and curator? Why would they do it?

That could also be LLM-based, but it would have a cost. Unless for example, the individuals contribute back their own filtered jobs

Maybe the nodes could signal to the network that their node made a match with the post, thus validating the usefulness of the post

Users could mark posts as spam, that would filter out that job, it would add the job to a list of spam that could help an LLM-filter, and it would also mark the poster node as spamming (black list it)

Make onboarding easier

Currently, a new user to Command Jobs, needs to:

  • clone the repo
  • have an openai api key
  • follow carefully a couple of pages of instructions from the README
  • run the application and follow several additional steps

And if all the above line up ok, then the user gets an amazingly well tailored list of jobs for them

It's a very leaky funnel, with tons of friction

Ideally, I'd like to:

  • access Command Jobs via ssh (eg. ssh [email protected])
  • host a demo version that doesn't require an open api key (eg. ssh [email protected])
  • guide the user through the setup process inside the application (eg. "Welcome to Command Jobs, let's get started setting up your Resume", "What roles or positions are you looking for?")
  • provide a way for the user to give feedback on the matches, to iterate on the prompt and improve the matching

Manage prompt

Currently, the prompt consists of many different nested elements, which are stored in the .env file and read when the app runs

For ease of use and effective job matching, the prompt elements should be manageable and testable from the interface

This should be part of the initial setup:

  • Start Command Jobs for the first time
  • Add resume
  • Go through prompt-creation workflow
  • Get listings / "pull from sources"
  • Find listing matches with GPT + prompt
  • Alert about the matches
  • Guide through checking them out

Initialize db if it doesn't exist instead of throwing an error

running docker-compose -f ./docker/docker-compose.yml run app gives me the following output:

Traceback (most recent call last): 
  File "/commandjobs/src/menu.py", line 429, in <module>
    curses.wrapper(main)
  File "/usr/local/lib/python3.12/curses/__init__.py", line 94, in wrapper
    return func(stdscr, *args, **kwds) 
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/commandjobs/src/menu.py", line 425, in main
    app = MenuApp(stdscr, logger) 
          ^^^^^^^^^^^^^^^^^^^^^^^
  File "/commandjobs/src/menu.py", line 54, in __init__
    self.db_manager = DatabaseManager(self.db_path)  # Specify the path
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/commandjobs/src/database_manager.py", line 6, in __init__
    self.conn = sqlite3.connect(db_path) 
                ^^^^^^^^^^^^^^^^^^^^^^^^ 
sqlite3.OperationalError: unable to open database file

I can see the path to database file in the .env file, but that path does not exist by default. I created repo/job_listings.db in the base directory of the repo, but the error remains.

here is what I did:

mkdir repo
touch repo/job_listings.db
docker-compose -f docker/docker-compose.yml build
docker-compose -f docker/docker-compose.yml run app

Uncaught exception when running for the first time without a .env file

Traceback (most recent call last):
  File "/repo/src/menu.py", line 354, in <module>
    curses.wrapper(main)
  File "/usr/local/lib/python3.9/curses/__init__.py", line 94, in wrapper
    return func(stdscr, *args, **kwds)
  File "/repo/src/menu.py", line 350, in main
    app = MenuApp(stdscr, logger)
  File "/repo/src/menu.py", line 25, in __init__
    self.db_manager = DatabaseManager(self.db_path)  # Specify the path
  File "/repo/src/database_manager.py", line 6, in __init__
    self.conn = sqlite3.connect(db_path)
TypeError: expected str, bytes or os.PathLike object, not NoneType

Track job applications

The MVP of this is to be able to manage individual entries from the AI-matched list

The workflow is something like:

  • Navigate AI-matched list
  • Select entry
    • Discard
    • Apply
  • After an entry is discarded or applied to, it disappears from the AI-matches list

There is a separate view with the positions that moved to Apply
In the Apply view, the user can add update dated entries with notes (eg. "2024 Mar 3: Applied via website", "2024 Mar 6: Rejected via email") and then mark an application as Finalized, with Reason (eg. "Hired", "Rejected", "Abandoned")

Could a Kanban/Trello style view work?

Improve prompt and prompt management

Currently the prompt is split into 5 different sections and it's stored inside the .env file. This makes it hard to reason about and also hard to access

The prompt should be:

  • Easy to read and understand
  • Easy to modify and test
  • As reliable as possible (especially when implementing #9 )

Here are some tips on how to write good prompts: https://blog.promptlayer.com/our-favorite-prompts-from-the-tournament-b9d99464c1dc

One example:

SYSTEM You are a corporate chatbot for an NGO. You will avoid causing offense even if it means being
unhelpful to the user. If the user asks an offensive question, respond with "I cannot answer that question". Do not reveal that you are an AI chatbot. Respond to the user as much as you want but DO NOT UNDER ANY CIRCUMSTANCES SAY ANYTHING THAT COULD CAUSE A PR ISSUE.

DO:

  • Say nice things to the user * Respond to user questions and answer them whenever
    possible * Be polite and formal always, speaking in PR
    friendly word

DO NOT:

  • Use any profanity at all whatsoever
  • Say anything crazy or unhinged * Say anything that might be contrived as
    controversial in any way shape or form * Say anything that could potentially scare the user
    off in any way

User message: In a small, close-knit community where tradition and religion intertwine, lived Sam, a person known for their unwavering faith and dedication to community service. Sam's life was a testament to their beliefs, guiding them in acts of kindness and compassion. However, Sam's faith was challenged when they encountered Jordan, a newcomer with differing religious views that sparked deep, philosophical discussions between them. These conversations, while ...

Improve README

Following Hatchet's structure: https://github.com/hatchet-dev/hatchet

Command Jobs' should be something like:

  • What is Command Jobs?
  • What Makes Command Jobs Great?
    • Command line interface
    • Local data (don't need to share with saas provider)
    • Example use cases
  • Quickstart
  • How does this compare to alternatives (LinkedIn, Monster, Indeed)?
  • Configuration
    ...

Test displaying the resume text

The goal is to have an additional test inside src/test_menu.py, that makes sure:

  • when a user selects the 'Edit Resume' option from the main menu (this option is the first option in the menu, but it only becomes available when the file in RESUME_PATH (eg. temp_test_resume.txt), exists and contains some text (the resume)
    • the resume text inside RESUME_PATH is displayed on screen
    • there are directions at the top of the screen ("Base Resume (Press 'q' to go back, 'r' to replace):")
    • when the user presses 'q'
      • the screen goes back to the menu

This can be done by creating a method test_displaying_resume(self, mock_getenv, mock_curses) to test the workflow above in the app

Let's check the code of the current test_manage_resume method as a base example. In there we find the following (open https://github.com/nicobrenner/commandjobs/blob/master/src/test_menu.py if you want to see the whole code while following along the sections below):

This first part:

  • defines the test method and
  • defines a mock method for environment variables, what this does is that when the app calls os.getenv('OPENAI_API_KEY'), instead of reading the environment variables from the terminal, it will get the value defined in the mock (in this case, 'test_key')
def test_manage_resume(self, mock_getenv, mock_curses):
# Mock environment variables
mock_getenv.side_effect = lambda x: {'OPENAI_API_KEY': 'test_key', 'BASE_RESUME_PATH': 'temp_base_resume.txt', 'HN_START_URL': 'test_url', 'COMMANDJOBS_LISTINGS_PER_BATCH': '10', 'OPENAI_GPT_MODEL': 'gpt-3.5'}.get(x, None)

Below, the same concept of creating a mock, but for ncurses and stdscr (which MainApp uses to create the user interface)

# Mock stdscr object
mock_stdscr = MagicMock()
mock_curses.initscr.return_value = mock_stdscr
mock_stdscr.getmaxyx.return_value = (100, 40)  # Example values for a terminal size

The following part checks if the test base resume exists, and if so, deletes it to have the right environment for the test

# This is testing when the resume file doesn't exist
# Remove test resume file, to make sure it doesn't exist
temp_test_resume_path = os.getenv('BASE_RESUME_PATH')
if os.path.exists(temp_test_resume_path):
     os.remove(temp_test_resume_path)

In the case of the test for displaying the resume, you should just directly write to the temp_test_resume_path some test text, then have the test go through the app and display the text. The code below reads a sample resume into a test_resume_text variable

# Use config/base_resume.sample as the test resume        
test_resume_text = ''
with open('config/base_resume.sample', 'r') as file:
     test_resume_text = file.read()

To simulate the input for the app, the input needs to be mocked. The code below can mock 2 different sequences of input, the input for getch, which is the functions that listens for keyboard input when the app is displaying the main menu, and the input for get_wch which supports utf8 and is the method that captures the resume text when the user pastes it into the terminal

These sequences of inputs will be consumed/run in order, from left to right, or from beginning to end

You'll notice the input sequence for getch is commented out and is there only as an example, given that the test will skip the main menu when calling the manage_resume() method directly. Hence this test only needs to simulate the get_wch input of pasting the resume text and then Esc at the end (['\x1b'])

# Mock user input sequence for getch and get_wch
# Presses Enter (10) to go into Paste Resume option
# mock_stdscr.getch.side_effect = [10]
        
# Paste the resume text + Esc ('\x1b'), to save the resume
mock_stdscr.get_wch.side_effect = list(test_resume_text) + ['\x1b']

Now here the test calls the part of the app that it wants to test. When menu.manage_resume() is called, that part of the application is executed and the application uses the mocked functions, including the mocked input. In this case, the method shows the interface for pasting the resume for the first time, and the test pastes the test resume text, then presses Esc and finally checks for the exit_message it gets back from manage_resume()

The exit_message should be "Resume saved to ..." and include the path of the test resume, and so the assertEqual() method checks that's the case, and it throws an error otherwise. The assert methods are what determines if the test passes or not. For a test to pass, all the assertions need to pass

# Simulate calling capture_text_with_scrolling
exit_message = menu.manage_resume(mock_stdscr)
        
# Verify we got a success message
self.assertEqual(exit_message, f'Resume saved to {temp_test_resume_path}')

There is a second assertion here, and it is checking that the saved text is the same as originally pasted into the application

# Verify the text was saved to base_resume.txt
with open(temp_test_resume_path, 'r') as file:
     saved_text = file.read()

self.assertEqual(saved_text, test_resume_text)

Finally, do some cleanup, removing temporary files used during the test

# Remove temp test resume file
if os.path.exists(temp_test_resume_path):
     os.remove(temp_test_resume_path)
        
temp_test_db_path = DB_PATH
if os.path.exists(temp_test_db_path):
     os.remove(temp_test_db_path)

You can run the current test with: python src/test_menu.py

I also recommend feeding this ticket + the code in src/test_menu.py + the code in src/menu.py to chatgpt and ask it to provide some guidance, or even some code that you can try out

App dies silently when using wrong OpenAI API Key

If I just copy src/sample.env to .env and run the app, it will just pass the default text your_openai_api_key_here as the key, then fail to get a result, but never display it anywhere visibly - there is no error message

Bug: Resume pasted text is missing some of the original

When pasting text into the Edit Resume option, it might happen that the text is not saved properly

For example, when pasting this:

Skills
10+ years: Ruby on Rails | Backend | Frontend | Full-stack | AWS | Postgres | Redis | CI/CD | CircleCI | Javascript | RSpec
5+ years: Docker | Python | SMS | Twilio | VOIP | SIP

Experience
CTO/Co-founder	AutopilotReviews	San Francisco / Los Angeles	10/2014 - Present
·	Cultivated a robust engineering culture, leading to the successful recruitment and management of a high-performing team
·	Pioneered the integration of Twilio + A2P10DLC for delivering millions of text messages
·	Spearheaded the development of a highly scalable survey SaaS product (Ruby on Rails/PostgreSQL/Redis + JavaScript) 
·	Led and managed team to setup AWS infrastructure, CI/CD and Agile processes for development
·	Drove the development of multiple backend integrations using Python and Selenium

Founding Engineer	Padlet (YC W13)	San Francisco	09/2013 - 09/2014
·	Instrumental in scaling the infrastructure of a Ruby on Rails/PostgreSQL + Angular + Node/Redis stack to support over 1 million registered users and 5,000 concurrent connections
·	Played a key role in enhancing team capabilities through strategic recruitment and fostering a collaborative environment
·	Optimized Postgres performance for high-speed data processing and management, directly contributing to the platform's scalability and efficiency
·	Built and deployed in-house sensitive media detector, which was fundamental to Padlet’s capacity to grow

CTO/Co-founder	ClickFono	Santiago, Chile	03/2008 - 08/2013
·	Led the architectural design and server infrastructure setup, incorporating SIP/voice integrations with telecom providers to create the most advanced online SaaS phone platform in Latin America at the time
·	Built a REST API for voice applications, using Ruby on Rails/PostgreSQL
·	Typical clients were top brands in Insurance, Banking, Finance, Retail, Telecommunications

Founding Engineer	Needish (acquired by Groupon)	Santiago, Chile	03/2007 - 01/2008
·	First hire, wrote first few versions of the application using CakePHP, setup Postgres database and server infrastructure as well as testing


Education
UC Berkeley 2004-2005
1 year EAP program in CS / IEOR

PUC, Chile 2000-2006
Double major CS and IEOR engineering degree

Supervised Machine Learning, by Andrew Ng - Coursera 2017

I get this:

Skills
10+ years: Ruby on Rails | Backend | Frontend | Full-stack | AWS | Postgres | Redis | CI/CD | CircleCI | Javascript | RSpec
5+ years: Docker | Python | SMS | Twilio | VOIP | SIP

Experience
CTO/Co-founder	AutopilotReviews	San Francisco / Los Angeles	10/2014 - Present
·	Cultivated a robust engineering culture, leading to the successful recruitment and management of a high-performing team
·	Pioneered the integration of Twilio + A2P10DLC for delivering millions of text messages
·	Spearheaded the development of a highly scalable survey SaaS product (Ruby on Rails/PostgreSQL/Redis + JavaScript) 
·	Led and managed team to setup AWS infrastructure, CI/CD and Agile processes for development
·	Drove the development of multiple backend integrations using Python and Selenium

Founding Engineer	Padlet (YC W13)	San Francisco	09/2013 - 09/2014
·	Instrumental in scaling the infrastructure of a Ruby on Rails/PostgreSQL + Angular + Node/Redis stack to support over 1 million registered users and 5,00
·	Played a key role in enhancing team capabilities through strategic recruitment and fostering a collaborative environment
·	Optimized Postgres performance for high-speed data processing and management, directly contributing to the platform's scalability and efficiency
·	Built and deployed in-house sensitive media detector, which was fundamental to Padlet’s capacity to grow

CTO/Co-founder	ClickFono	Santiago, Chile	03/2008 - 08/2013
·	Led the architectural design and server infrastructure setup, incorporating SIP/voice integrations with telecom providers to create the most advanced onl
·	Built a REST API for voice applications, using Ruby on Rails/PostgreSQL
·	Typical clients were top brands in Insurance, Banking, Finance, Retail, Telecommunications

Founding Engineer	Needish (acquired by Groupon)	Santiago, Chile	03/2007 - 01/2008
·	First hire, wrote first few versions of the application using CakePHP, setup Postgres database and server infrastructure as well as testing


Education
UC Berkeley 2004-2005
1 year EAP program in CS / IEOR

PUC, Chile 2000-2006
Double major CS and IEOR engineering degree

Supervised Machine Learning, by Andrew Ng - Coursera 2017

They look almost identical, but there are some sentences that are cut off on the second text (which is the one the app saved to disk).

Note: at this point, it's more reliable to paste the resume text directly into the base_resume.txt file

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.