Update generate_post.py
This commit is contained in:
parent
3af56857c2
commit
e3981d8269
1 changed files with 101 additions and 60 deletions
161
generate_post.py
161
generate_post.py
|
|
@ -1,5 +1,6 @@
|
|||
import os
|
||||
import csv
|
||||
import re
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
from fpdf import FPDF
|
||||
|
|
@ -7,31 +8,30 @@ from fpdf import FPDF
|
|||
# -------------------------------------------------------------------------
|
||||
# CODE & CANVAS BRAND DESIGN TOKENS
|
||||
# -------------------------------------------------------------------------
|
||||
COLOR_LINEN = (250, 248, 245) # #FAF8F5 - Default background canvas[span_2](start_span)[span_2](end_span)
|
||||
COLOR_NAVY = (26, 35, 50) # #1A2332 - Deep structural headers[span_3](start_span)[span_3](end_span)
|
||||
COLOR_TERRACOTTA = (211, 107, 77) # #D36B4D - Primary visual accents[span_4](start_span)[span_4](end_span)
|
||||
COLOR_CHARCOAL = (43, 43, 43) # #2B2B2B - High-legibility body text[span_5](start_span)[span_5](end_span)
|
||||
COLOR_BORDER = (229, 222, 201) # #E5DEC9 - Thin muted gridlines[span_6](start_span)[span_6](end_span)
|
||||
COLOR_LINEN = (250, 248, 245) # #FAF8F5 - Default background canvas
|
||||
COLOR_NAVY = (26, 35, 50) # #1A2332 - Deep structural base color
|
||||
COLOR_TERRACOTTA = (211, 107, 77) # #D36B4D - Primary visual accents
|
||||
COLOR_CHARCOAL = (43, 43, 43) # #2B2B2B - High-legibility body text
|
||||
COLOR_BORDER = (229, 222, 201) # #E5DEC9 - Thin muted gridlines
|
||||
|
||||
CSV_URL = "https://raw.githubusercontent.com/CodeAndCanvas728/repobase/refs/heads/main/RepoDatabase.csv"
|
||||
LOCAL_CSV_PATH = "RepoDatabase.csv"
|
||||
REMOTE_CSV_URL = "https://raw.githubusercontent.com/CodeAndCanvas728/repobase/refs/heads/main/RepoDatabase.csv"
|
||||
|
||||
class CodeCanvasPDF(FPDF):
|
||||
def header(self):
|
||||
# Establish Editorial Minimalism and generous margins
|
||||
self.set_fill_color(*COLOR_LINEN)
|
||||
self.rect(0, 0, 210, 297, 'F')
|
||||
|
||||
# Subtle top accent line instead of heavy borders
|
||||
self.set_draw_color(*COLOR_BORDER)
|
||||
self.set_line_width(0.5)
|
||||
self.line(15, 15, 195, 15)
|
||||
|
||||
# Standalone Ampersand Shorthand as the brand anchor
|
||||
# Standalone Ampersand Shorthand
|
||||
self.set_text_color(*COLOR_TERRACOTTA)
|
||||
self.set_font("Times", "I", 16) # Elegant serif substitution for Georgia
|
||||
self.set_font("Times", "I", 16) # Classic serif
|
||||
self.text(15, 25, "&")
|
||||
self.set_text_color(*COLOR_NAVY)
|
||||
self.set_font("Helvetica", "B", 8) # Sans-serif for technical meta-tags
|
||||
self.set_font("Helvetica", "B", 8) # Sans-serif technical data
|
||||
self.text(23, 23, "CODE & CANVAS | INSIGHTS")
|
||||
|
||||
def footer(self):
|
||||
|
|
@ -40,82 +40,122 @@ class CodeCanvasPDF(FPDF):
|
|||
self.text(185, 285, f"Page {self.page_no()}")
|
||||
|
||||
def clean_typography(text):
|
||||
"""Enforces the strict micro-styling rule: Absolutely no em-dashes."""
|
||||
"""Enforces absolute purity of text: No unicode or markdown em-dashes."""
|
||||
return text.replace("—", " : ").replace("--", " - ")
|
||||
|
||||
def fetch_subject():
|
||||
try:
|
||||
response = requests.get(CSV_URL, timeout=10)
|
||||
if response.status_code == 200 and response.text.strip():
|
||||
lines = response.text.splitlines()
|
||||
reader = csv.DictReader(lines)
|
||||
def fetch_and_update_subject():
|
||||
"""Reads local repository CSV to find an unused row, updates it to 'yes',
|
||||
and writes it back immediately to prevent duplicate scheduling entries."""
|
||||
rows = []
|
||||
selected_subject = None
|
||||
file_exists_locally = os.path.exists(LOCAL_CSV_PATH)
|
||||
|
||||
# 1. Read Database Data
|
||||
if file_exists_locally:
|
||||
with open(LOCAL_CSV_PATH, mode='r', newline='', encoding='utf-8') as f:
|
||||
reader = csv.DictReader(f)
|
||||
fieldnames = reader.fieldnames
|
||||
for row in reader:
|
||||
if row.get('Used', '').lower() == 'no':
|
||||
return {
|
||||
# Find the first unused item matching criteria
|
||||
if not selected_subject and row.get('Used', '').strip().lower() == 'no':
|
||||
row['Used'] = 'yes' # State Update Mutation
|
||||
selected_subject = {
|
||||
"title": row['Title'],
|
||||
"desc": row['Description'],
|
||||
"points": [row['Point1'], row['Point2'], row['Point3']],
|
||||
"url": row.get('URL', '')
|
||||
"url": row.get('URL', f"https://github.com/search?q={row['Title']}")
|
||||
}
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Fallback Logic: Scrape GitHub Trending AI/LLM Python/Go repositories
|
||||
try:
|
||||
fb_res = requests.get("https://github.com/trending", timeout=10)
|
||||
soup = BeautifulSoup(fb_res.text, 'html.parser')
|
||||
repo = soup.find('article', class_='Box-row')
|
||||
title = repo.find('h2', class_='h3').text.strip().replace('\n', '').replace(' ', '')
|
||||
desc = repo.find('p', class_='col-9').text.strip()
|
||||
return {
|
||||
"title": title,
|
||||
"desc": desc,
|
||||
"points": ["Open-source community standard", "Highly scalable architecture", "Streamlined local environment deployment"],
|
||||
"url": f"https://github.com/{title}"
|
||||
}
|
||||
except Exception:
|
||||
return {
|
||||
"title": "Model Context Protocol",
|
||||
"desc": "An open standard enabling secure, structured data communication layers between local environments and LLMs.",
|
||||
"points": ["Eliminates custom integration glue code", "Maintains clean architectural boundaries", "Built for developer tool interoperability"],
|
||||
"url": "https://github.com/modelcontextprotocol"
|
||||
}
|
||||
rows.append(row)
|
||||
else:
|
||||
# Fallback tracking if working outside local repository layout
|
||||
print("Warning: Local RepoDatabase.csv not found. Reverting to remote live endpoint.")
|
||||
try:
|
||||
res = requests.get(REMOTE_CSV_URL, timeout=10)
|
||||
if res.status_code == 200:
|
||||
lines = res.text.splitlines()
|
||||
reader = csv.DictReader(lines)
|
||||
fieldnames = reader.fieldnames
|
||||
for row in reader:
|
||||
if not selected_subject and row.get('Used', '').strip().lower() == 'no':
|
||||
selected_subject = {
|
||||
"title": row['Title'],
|
||||
"desc": row['Description'],
|
||||
"points": [row['Point1'], row['Point2'], row['Point3']],
|
||||
"url": row.get('URL', '')
|
||||
}
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 2. Save Updated Database State if modified locally
|
||||
if selected_subject and file_exists_locally:
|
||||
with open(LOCAL_CSV_PATH, mode='w', newline='', encoding='utf-8') as f:
|
||||
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
||||
writer.writeheader()
|
||||
writer.writerows(rows)
|
||||
return selected_subject
|
||||
|
||||
# 3. Dynamic GitHub Scraping Fallback Routine
|
||||
if not selected_subject:
|
||||
print("Database exhausted or unreachable. Querying trending open-source indexes...")
|
||||
try:
|
||||
fb_res = requests.get("https://github.com/trending", timeout=10)
|
||||
soup = BeautifulSoup(fb_res.text, 'html.parser')
|
||||
repo = soup.find('article', class_='Box-row')
|
||||
raw_title = repo.find('h2', class_='h3').text.strip().replace('\n', '').replace(' ', '')
|
||||
desc = repo.find('p', class_='col-9').text.strip()
|
||||
return {
|
||||
"title": raw_title.split('/')[-1],
|
||||
"desc": desc,
|
||||
"points": ["Open-source workflow standard", "Highly modular architectural design", "Streamlined environment footprint"],
|
||||
"url": f"https://github.com/{raw_title}"
|
||||
}
|
||||
except Exception:
|
||||
# Absolute hard-fail recovery state
|
||||
return {
|
||||
"title": "Model Context Protocol",
|
||||
"desc": "An open standard enabling secure, structured data communication layers between local environments and generative applications.",
|
||||
"points": ["Eliminates custom integration glue code", "Maintains clear architectural boundaries", "Built for cross-tool interoperability"],
|
||||
"url": "https://github.com/modelcontextprotocol"
|
||||
}
|
||||
|
||||
def generate_assets():
|
||||
data = fetch_subject()
|
||||
data = fetch_and_update_subject()
|
||||
title = clean_typography(data['title'])
|
||||
desc = clean_typography(data['desc'])
|
||||
points = [clean_typography(p) for p in data['points']]
|
||||
|
||||
# Target path configuration
|
||||
os.makedirs("_drafts", exist_ok=True)
|
||||
safe_date = "weekly-draft"
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 1. GENERATE THE BRANDED PDF
|
||||
# PDF COMPILATION BLOCK
|
||||
# -------------------------------------------------------------------------
|
||||
pdf = CodeCanvasPDF()
|
||||
pdf.set_margins(15, 30, 15)
|
||||
pdf.add_page()
|
||||
|
||||
# Title Layout (High-contrast Serif Display Style)
|
||||
# Header display layout
|
||||
pdf.set_y(40)
|
||||
pdf.set_text_color(*COLOR_NAVY)
|
||||
pdf.set_font("Times", "", 28) # Georgia alternative
|
||||
pdf.set_font("Times", "", 26)
|
||||
pdf.multi_cell(180, 12, title)
|
||||
|
||||
# Accent Divider Line
|
||||
# Layout dividing grid rules
|
||||
pdf.set_draw_color(*COLOR_BORDER)
|
||||
pdf.line(15, pdf.get_y() + 5, 80, pdf.get_y() + 5)
|
||||
|
||||
# Brief Informational Summary
|
||||
# Copy layout execution
|
||||
pdf.set_y(pdf.get_y() + 15)
|
||||
pdf.set_text_color(*COLOR_CHARCOAL)
|
||||
pdf.set_font("Helvetica", "", 11) # Sans-serif structural body text
|
||||
pdf.set_font("Helvetica", "", 11)
|
||||
pdf.multi_cell(180, 7, desc)
|
||||
|
||||
# Structured Grid for Key Highlights
|
||||
# Balanced spacing grids for parameters
|
||||
pdf.set_y(pdf.get_y() + 15)
|
||||
for idx, pt in enumerate(points, 1):
|
||||
pdf.set_draw_color(*COLOR_BORDER)
|
||||
pdf.set_fill_color(*COLOR_LINEN)
|
||||
# Structural thin-bordered card layouts
|
||||
pdf.rect(15, pdf.get_y(), 180, 18, 'DF')
|
||||
|
||||
pdf.set_x(20)
|
||||
|
|
@ -128,11 +168,11 @@ def generate_assets():
|
|||
pdf.cell(160, 18, pt)
|
||||
pdf.set_y(pdf.get_y() + 22)
|
||||
|
||||
pdf_filename = "linkedin_insight_asset.pdf"
|
||||
pdf.output(pdf_filename)
|
||||
pdf_path = f"_drafts/{safe_date}.pdf"
|
||||
pdf.output(pdf_path)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 2. GENERATE LINKEDIN POST COPY
|
||||
# MARKDOWN CONVERSION BLOCK
|
||||
# -------------------------------------------------------------------------
|
||||
post_copy = f"""💡 Weekly AI Architecture Breakdown
|
||||
|
||||
|
|
@ -152,10 +192,11 @@ Repository Link: {data['url']}
|
|||
|
||||
#SoftwareArchitecture #DeveloperTools #TechInsights #CodeAndCanvas"""
|
||||
|
||||
print("--- LINKEDIN POST DRAFT ---")
|
||||
print(post_copy)
|
||||
print("\n--- ASSET STATUS ---")
|
||||
print(f"Success: {pdf_filename} generated inside your directory following brand design guidelines.")
|
||||
md_path = f"_drafts/{safe_date}.md"
|
||||
with open(md_path, mode='w', encoding='utf-8') as f:
|
||||
f.write(post_copy)
|
||||
|
||||
print(f"Workflow Complete: Created tracking drafts inside configuration paths.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
generate_assets()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue