# Project export: JobCat

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 11.0
- Tagline: Feeling overwhelmed with the job search process? Land on your feet with JobCat!
- Devpost: https://devpost.com/software/jobcat
- GitHub: https://github.com/avivamei/calhacks
- Video: https://www.youtube.com/embed/_h906mlX6a4?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — Jennifer Nguyen (19 commits), xavajk (9 commits), avivamei (4 commits)

## Devpost submission (written by the team)

### Inspiration

As our graduation date looms close, we are in the thick of the job search process, and tracking our job applications has a myriad of benefits that keep us proactive and organized during the process. Tracking helps us monitor deadlines and better prepare for interviews by retaining key details from job descriptions. Tracking also helps prevent sending duplicate applications to the same company and shows our progress over time, keeping us motivated and accountable during the often lengthy job hunt. However, with large amounts of job applications, tracking each one manually can be extremely tedious and quickly become overwhelming.

### What it does

Enter JobCat, your personal job search assistant. JobCat keeps your application information organized and easy to access. It automatically scans and summarizes your job application status directly from your email inbox. JobCat tracks the company, position, mode (remote, onsite, etc.), location, salary, application date, status (interview, offer, rejected, etc.), and job description for a job application.

### How we built it

JobCat is built with Reflex, Python, LangChain, and Groq. Our cat mascot was made using Figma.

### Challenges we ran into

Unfamiliar frameworks and API: Reflex, LangChain, Groq Slow WiFi Running Python on Windows

### Accomplishments we're proud of

Dynamic updates UI Design AI integration

### What we learned

Reflex LangChain Groq

### What's next

There are many additional features we hope to add to further enhance the job search experience: SMS notifications for application status updates Tailored interview prep based on the job posting and the user's experience Custom AI-powered resume and cover letter building

## README (from the GitHub repository)

# JobCat

JobCat keeps your application information organized and easy to access. It automatically scans and summarizes your job application status directly from your email inbox. It tracks the company, position, mode (remote, onsite, etc.), location, salary, application date, status (interview, offer, rejected, etc.), and job description for a job application.

## Installation 

Clone the repo.

Install dependencies:

```bash
`pip install -r requirements.txt`.
`pip install -r ai\requirements.txt`.
```

Migrate your database:

```bash
reflex db makemigrations
reflex db migrate
```

Run App:
```bash
reflex run
```



## Detected evidence (automated analysis)

Indexed codebase: 47 recognized source files, 104 KB.
- CSS (language) — detected in the code
- Python (language) — detected in the code
- LangChain (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (57 of 57)

```
.DS_Store
.gitignore
ai/__init__.py
ai/.DS_Store
ai/.env
ai/email_parser.py
ai/gmail.py
ai/oauth/client_secret_163202554535-p6pfh8qs471c2b10tusiqr4p8gnn48qi.apps.googleusercontent.com.json
ai/requirements.txt
ai/search_promtps.py
alembic.ini
alembic/env.py
alembic/README
alembic/script.py.mako
alembic/versions/277cad49d2b0_.py
alembic/versions/333b61328fdd_.py
alembic/versions/7aaec6b87d88_.py
alembic/versions/e565fdc23e6c_something_changed.py
assets/styles.css
calhacks/__init__.py
calhacks/backend/__init__.py
calhacks/backend/backend.py
calhacks/calhacks.py
calhacks/components/__init__.py
calhacks/components/card.py
calhacks/components/form_field.py
calhacks/components/form_text_area.py
calhacks/components/heading.py
calhacks/components/login_card.py
calhacks/components/mode_badges.py
calhacks/components/notification.py
calhacks/components/profile_input.py
calhacks/components/sidebar.py
calhacks/components/stats_cards.py
calhacks/components/status_badges.py
calhacks/pages/__init__.py
calhacks/pages/about.py
calhacks/pages/dashboard.py
calhacks/pages/landing.py
calhacks/pages/profile.py
calhacks/pages/settings.py
calhacks/pages/table.py
calhacks/styles.py
calhacks/templates/__init__.py
calhacks/templates/template.py
calhacks/views/__init__.py
calhacks/views/charts.py
calhacks/views/color_picker.py
calhacks/views/navbar.py
calhacks/views/radius_picker.py
calhacks/views/scaling_picker.py
calhacks/views/stats_cards.py
calhacks/views/table.py
calhacks/views/work_mode_chart.py
README.md
requirements.txt
rxconfig.py
```

### Dependencies

- ai/requirements.txt: langchain-google-community[gmail], langchain-groq, python-dotenv
- requirements.txt: beautifulsoup4, psycopg2-binary, reflex@>=0.5.3, reflex_google_auth

### Recent commits (newest first)

- whoops forgot to add these
- Update README.md
- Merge branch 'main' of github.com:avivamei/calhacks into main
- update requirements and gitignore
- Update installation instructions in README.md
- Merge pull request #9 from avivamei/description
- dynamic charts
- Merge branch 'main' of https://github.com/avivamei/calhacks
- test cases
- Merge pull request #8 from avivamei/description
- updating dashboards and FE
- Merge branch 'main' of https://github.com/avivamei/calhacks
- oauth fe integration
- Merge pull request #7 from avivamei/description
- add description and pop up
- add description
- fix db columns
- Merge pull request #6 from avivamei/ui
- add styles, logout
- add sidebar and stats to dashboard

## Key source files (fetched from GitHub, selected and truncated for size)

### requirements.txt

```
reflex>=0.5.3
psycopg2-binary
reflex_google_auth 
beautifulsoup4
```

### ai/requirements.txt

```
langchain-groq
python-dotenv
langchain-google-community[gmail]
```

### rxconfig.py

```python
import reflex as rx

config = rx.Config(
    app_name="calhacks",
    db_url="sqlite:///reflex.db",
)
```

### assets/styles.css

```css
body {
    overflow-y: scroll;
}

.recharts-cartesian-grid-horizontal line {
	stroke-dasharray: 0;
	stroke-opacity: 0.6 !important;
	stroke: var(--gray-7);
}

.recharts-cartesian-grid-vertical line {
	stroke-opacity: 0 !important;
}

.rt-BaseTabList {
	box-shadow: none;
}

```

### calhacks/calhacks.py

```python
import reflex as rx

from . import pages
from . import styles

from .pages.dashboard import dashboard
from .pages.landing import landing
from .views.navbar import navbar

def index() -> rx.Component:
    return landing(),
    

app = rx.App(
    style=styles.base_style,
    stylesheets=styles.base_stylesheets,

)

app.add_page(
    index, 
    title="Login"
)





```

### ai/gmail.py

```python
# from email_monitor import EmailMonitor

# monitor = EmailMonitor(credentials_file="oauth/client_secret_163202554535-p6pfh8qs471c2b10tusiqr4p8gnn48qi.apps.googleusercontent.com.json")
# email = monitor.search_mail(
#     query={ "from": "notifications-noreply@linkedin.com" },
#     wait_for_match=True,
#     unread=False,
#     labels=["INBOX"],
#     delay=10
# )
# print(email)

from langchain_google_community import GmailToolkit
from langchain_google_community.gmail.utils import build_resource_service, get_gmail_credentials

# setup Gmail toolkit
credentials = get_gmail_credentials(
    scopes=["https://mail.google.com/"],
    client_secrets_file="oauth/client_secret_163202554535-p6pfh8qs471c2b10tusiqr4p8gnn48qi.apps.googleusercontent.com.json",
)
api_resource = build_resource_service(credentials=credentials)
gmail_toolkit = GmailToolkit(api_resource=api_resource)
gmail_tools = gmail_toolkit.get_tools()

```

### ai/search_promtps.py

```python
BASE_SEARCH = '''Given the following examples for search queries, formulate a search query to retrieve emails related to a new or in progress job application.\n\nThe Gmail query. Example filters include, after:year/mo/date, before:year/mo/date, \"exact phrase\". Search newer/older than using d (day), m (month), and y (year): newer_than:2d, older_than:1y. To combine multiple filters, use OR. Aside from temporal filters, only search using exact phrases.'''

NEW_APPLICATIONS = '''Make a tool single call using the search_gmail tool. Search for emails where the subject or body are referring to a newly submitted job application. Here are some examples of exact phrases to look for:\n\n\"thank you for applying\" OR \"you've applied\" OR \"we have received your application\". You may use a combination or subset of these filters along with any permutation of them aligning with their sentiment as you see fit. Search for emails in the last {cutoff}. Pass in {max_results} to the `max_results` tool argument.'''
```

### alembic/env.py

```python
from logging.config import fileConfig

from alembic import context
from sqlalchemy import engine_from_config, pool

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config

# Interpret the config file for Python logging.
# This line sets up loggers basically.
if config.config_file_name is not None:
    fileConfig(config.config_file_name)

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
target_metadata = None

# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.


def run_migrations_offline() -> None:
    """Run migrations in 'offline' mode.

    This configures the context with just a URL
    and not an Engine, though an Engine is acceptable
    here as well.  By skipping the Engine creation
    we don't even need a DBAPI to be available.

    Calls to context.execute() here emit the given string to the
    script output.

    """
    url = config.get_main_option("sqlalchemy.url")
    context.configure(
        url=url,
        target_metadata=target_metadata,
        literal_binds=True,
        dialect_opts={"paramstyle": "named"},
    )

    with context.begin_transaction():
        context.run_migrations()


def run_migrations_online() -> None:
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """
    connectable = engine_from_config(
        config.get_section(config.config_ini_section, {}),
        prefix="sqlalchemy.",
        poolclass=pool.NullPool,
    )

    with connectable.connect() as connection:
        context.configure(connection=connection, target_metadata=target_metadata)

        with context.begin_transaction():
            context.run_migrations()


if context.is_offline_mode():
    run_migrations_offline()
else:
    run_migrations_online()

```

### calhacks/styles.py

```python
"""Styles for the app."""

import reflex as rx

border_radius = "var(--radius-2)"
border = f"1px solid {rx.color('gray', 5)}"
text_color = rx.color("gray", 11)
gray_color = rx.color("gray", 11)
gray_bg_color = rx.color("gray", 3)
accent_text_color = rx.color("accent", 10)
accent_color = rx.color("accent", 1)
accent_bg_color = rx.color("accent", 3)
hover_accent_color = {"_hover": {"color": accent_text_color}}
hover_accent_bg = {"_hover": {"background_color": accent_color}}
content_width_vw = "90vw"
sidebar_width = "32em"
sidebar_content_width = "16em"
max_width = "1480px"
color_box_size = ["2.25rem", "2.25rem", "2.5rem"]


template_page_style = {
    "padding_top": ["1em", "1em", "2em"],
    "padding_x": ["auto", "auto", "2em"],
}

template_content_style = {
    "padding": "1em",
    "margin_bottom": "2em",
    "min_height": "90vh",
}

link_style = {
    "color": accent_text_color,
    "text_decoration": "none",
    **hover_accent_color,
}

overlapping_button_style = {
    "background_color": "white",
    "border_radius": border_radius,
}

markdown_style = {
    "code": lambda text: rx.code(text, color_scheme="gray"),
    "codeblock": lambda text, **props: rx.code_block(text, **props, margin_y="1em"),
    "a": lambda text, **props: rx.link(
        text,
        **props,
        font_weight="bold",
        text_decoration="underline",
        text_decoration_color=accent_text_color,
    ),
}

notification_badge_style = {
    "width": "1.25rem",
    "height": "1.25rem",
    "display": "flex",
    "align_items": "center",
    "justify_content": "center",
    "position": "absolute",
    "right": "-0.35rem",
    "top": "-0.35rem",
}

ghost_input_style = {
    "--text-field-selection-color": "",
    "--text-field-focus-color": "transparent",
    "--text-field-border-width": "1px",
    "background_clip": "content-box",
    "background_color": "transparent",
    "box_shadow": "inset 0 0 0 var(--text-field-border-width) transparent",
    "color": "",
}

box_shadow_style = "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)"

color_picker_style = {
    "border_radius": "max(var(--radius-3), var(--radius-full))",
    "box_shadow": box_shadow_style,
    "cursor": "pointer",
    "display": "flex",
    "align_items": "center",
    "justify_content": "center",
    "transition": "transform 0.15s ease-in-out",
    "_active": {
        "transform": "translateY(2px) scale(0.95)",
    },
}


base_stylesheets = [
    "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap",
    "styles.css",
]

base_style = {
    "font_family": "Inter",
}

```

### calhacks/templates/__init__.py

```python
from .template import ThemeState, template

```

[33 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]