Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@vinli0921
Copy link
Contributor

@vinli0921 vinli0921 commented May 2, 2025

What does this PR do?

This PR fixes the issue with hyperlink extraction from Google Sheets by implementing a dual approach to handle both formula-based and UI-inserted hyperlinks. Previously, the system was only able to extract hyperlinks created using the =HYPERLINK() formula, resulting in missing links for cells where hyperlinks were inserted through the Google Sheets UI. The fix:

  • Enhances JobPostingService.getFinanceJobs() to try both hyperlink extraction methods
  • Adds get_sheet_with_grid_data() method to fetch complete cell metadata including hyperlink information
  • Adds get_hyperlink_from_grid_data() method to extract hyperlinks from various possible locations in cell metadata
  • Improves error handling for cases where row data might be irregular or incomplete

Additionally, this PR implements significant UI improvements to the newsletter:

  • Completely redesigned newsletter styling to match brand requirements
  • Added dedicated banner images for each section (main header, job opportunities, tech, finance, consulting, marketing)
  • Centered the newsletter layout with properly aligned content
  • Added detailed job source information with clickable repository links
  • Improved job listing format with clearer company/role information
  • Distinguished between "Posted" dates for most jobs and "Deadline" dates specifically for finance positions
  • Fixed display issues with the newsletter header
  • Enhanced overall visual hierarchy and readability

This change ensures all job links can be properly extracted from the RecruitU spreadsheet regardless of how they were created in Google Sheets, while also delivering a significantly improved visual experience for newsletter recipients.

Type of change

  • Fix: Bug fix (non-breaking change which fixes an issue)
  • Refactor: Any code refactoring
  • Chore: technical debt, workflow improvements
  • Feature: New feature (non-breaking change which adds functionality)
  • Documentation: This change requires a documentation update
  • Merge: Pushing features to an upper envrironment

Tests Performed

  1. Verified extraction of formula-based hyperlinks (=HYPERLINK()) works as before
  2. Confirmed extraction of UI-inserted hyperlinks now works properly
  3. Tested with the RecruitU spreadsheet (ID: 15za1luZR08YmmBIFOAk6-GJB3T22StEuiZgFFuJeKW0) to ensure all links are correctly retrieved
  4. Validated error handling for rows with missing or malformed data
  5. Updated test_job_posting_service unit test to include new Google Sheets functionality

Screenshots

Screenshot 2025-05-02 at 7 46 42 PM

Additional Comments

@github-actions
Copy link

github-actions bot commented May 2, 2025

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
2649 1981 75% 70% 🟢

New Files

No new covered files...

Modified Files

File Coverage Status
chalicelib/api/broadcast.py 48% 🟢
chalicelib/modules/google_sheets.py 22% 🟢
chalicelib/services/BroadcastService.py 23% 🟢
chalicelib/services/JobPostingService.py 87% 🟢
chalicelib/utils.py 64% 🟢
tests/services/test_job_posting_service.py 95% 🟢
TOTAL 57% 🟢

updated for commit: bfe2dd0 by action🐍


# Common CSS for styling the newsletter sections
# Updated CSS for styling the newsletter to match the provided image
css = """
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to modularize or put this into utils where you can call that constant? This takes way too much space and it's hard to understand the core functionality.

</style>
"""

# Job sources text
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the job sources change in the long run? I think it'd be easier to maintain if we had a const tracking the links and names, something along the lines of:

JOB_SOURCE_LIST = {
  "tech": [{name: "...", link: "..."}, ...],
  "finance": [...],
  ...
}

and you can iterate through it and embed as an fstring. If it's too complicated, feel free to leave it be.

"""
JOBS_SHEET_ID = "15za1luZR08YmmBIFOAk6-GJB3T22StEuiZgFFuJeKW0"
try:
# First get the sheet data with FORMULA render option to parse hyperlink formulas
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "Get sheet data with FORMULA render option to parse hyperlink formulas"

sheet_data = self.gs.get_sheet_with_grid_data(JOBS_SHEET_ID, "2027 Opportunities Tracker")

# 2. Separate headers from rows
headers = response["values"][5]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated to PR changes, but I'm wondering if we should create consts like HEADER_DIVIDER (maybe there's a better naming) to use instead of numbers so it'd be response["values"][HEADER_DIVIDER]. Thoughts?


return

beta_list = ["[email protected]",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of this? I don't see it being used anywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may have been an accidental commit. I wanted to keep this list locally so I can send periodic test emails to the listed recipients

Copy link
Contributor

@jinyoungbang jinyoungbang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding more reviews - let me know if you have any questions!

return True
return False
except Exception:
# hotfix for if dateStr is not in the format "Mon DD", but intead is in the format 'DDd', i.e. "15d"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: fix spelling

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is a temporary hotfix, can you write a TODO inside?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

# Return empty grid data, as our test is using formula-based hyperlinks
return {"sheets": [{"data": [{"rowData": []}]}]}

def get_hyperlink_from_grid_data(self, sheet_data, row_idx, col_idx):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I might be missing some context, but if this won't be used, what's the purpose of this fixture/function?

@jinyoungbang
Copy link
Contributor

@vinli0921 Please update the PR name to be more descriptive!

@vinli0921 vinli0921 changed the title Feat/update styling [Newsletter] changes to broadcast and job posting services, along with added google sheets functionality May 3, 2025
assert job_service.isMoreThanOneWeekAgo(date_old) is True


def test_get_finance_jobs_ui_hyperlink(job_service):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, however test code is a bit verbose. In cases like this, we usually mock the return values, and inject them. Here is an example of what I'd do (but I have no clue if this technically works as there might be more dependencies involved):

from unittest.mock import MagicMock # Import this built-in Python mock library

def test_get_finance_jobs_ui_hyperlink(job_service):
    service = job_service
   
    mocked_gs = MagicMock()
    mocked_gs.get_all_cells.return_value =  {
        "values": [
            [], [], [], [], [],
            ["Company", "Opportunity", "Link", "Deadline"],
            ["CompanyC", "Analyst", 'Click here', "44197"]
        ]
    }
    mocked_gs.get_sheet_with_grid_data.return_value = {"sheets": [{"data": [{"rowData": []}]}]}
    mocked_gs.get_hyperlink_from_grid_data.return_value = "http://ui-inserted-link.com"

    service.gs = mocked_gs  # Inject mocked dependency

    jobs = service.getFinanceJobs()

    assert len(jobs) == 1
    assert jobs[0]["company"] == "CompanyC"
    assert jobs[0]["role"] == "Analyst"
    assert jobs[0]["link"] == "http://ui-inserted-link.com"
    assert jobs[0]["date"] == "Jan 01"

If refactoring is too complicated, just leave it be but consider it for next time!


job_service.gs = DummyGS()

jobs = job_service.getFinanceJobs()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idk how I didn't catch this last time, but all methods in Python (per style guide) should be in snake_case – maybe you can create a chore PR just fixing this but putting it out there for consistency purposes.

@vinli0921 vinli0921 merged commit 7559080 into dev/v3.0 May 28, 2025
1 check passed
@vinli0921 vinli0921 deleted the feat/update_styling branch May 28, 2025 14:46
@jinyoungbang jinyoungbang added the improvement Enhancements label Jun 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Enhancements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants