Update generate_post.py

This commit is contained in:
CodeAndCanvas728 2026-06-17 16:58:35 +02:00 committed by GitHub
parent 3af56857c2
commit e3981d8269
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,5 +1,6 @@
import os import os
import csv import csv
import re
import requests import requests
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from fpdf import FPDF from fpdf import FPDF
@ -7,31 +8,30 @@ from fpdf import FPDF
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# CODE & CANVAS BRAND DESIGN TOKENS # CODE & CANVAS BRAND DESIGN TOKENS
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
COLOR_LINEN = (250, 248, 245) # #FAF8F5 - Default background canvas[span_2](start_span)[span_2](end_span) COLOR_LINEN = (250, 248, 245) # #FAF8F5 - Default background canvas
COLOR_NAVY = (26, 35, 50) # #1A2332 - Deep structural headers[span_3](start_span)[span_3](end_span) COLOR_NAVY = (26, 35, 50) # #1A2332 - Deep structural base color
COLOR_TERRACOTTA = (211, 107, 77) # #D36B4D - Primary visual accents[span_4](start_span)[span_4](end_span) COLOR_TERRACOTTA = (211, 107, 77) # #D36B4D - Primary visual accents
COLOR_CHARCOAL = (43, 43, 43) # #2B2B2B - High-legibility body text[span_5](start_span)[span_5](end_span) COLOR_CHARCOAL = (43, 43, 43) # #2B2B2B - High-legibility body text
COLOR_BORDER = (229, 222, 201) # #E5DEC9 - Thin muted gridlines[span_6](start_span)[span_6](end_span) 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): class CodeCanvasPDF(FPDF):
def header(self): def header(self):
# Establish Editorial Minimalism and generous margins
self.set_fill_color(*COLOR_LINEN) self.set_fill_color(*COLOR_LINEN)
self.rect(0, 0, 210, 297, 'F') self.rect(0, 0, 210, 297, 'F')
# Subtle top accent line instead of heavy borders
self.set_draw_color(*COLOR_BORDER) self.set_draw_color(*COLOR_BORDER)
self.set_line_width(0.5) self.set_line_width(0.5)
self.line(15, 15, 195, 15) self.line(15, 15, 195, 15)
# Standalone Ampersand Shorthand as the brand anchor # Standalone Ampersand Shorthand
self.set_text_color(*COLOR_TERRACOTTA) 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.text(15, 25, "&")
self.set_text_color(*COLOR_NAVY) 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") self.text(23, 23, "CODE & CANVAS | INSIGHTS")
def footer(self): def footer(self):
@ -40,18 +40,44 @@ class CodeCanvasPDF(FPDF):
self.text(185, 285, f"Page {self.page_no()}") self.text(185, 285, f"Page {self.page_no()}")
def clean_typography(text): 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("--", " - ") return text.replace("", " : ").replace("--", " - ")
def fetch_subject(): def fetch_and_update_subject():
try: """Reads local repository CSV to find an unused row, updates it to 'yes',
response = requests.get(CSV_URL, timeout=10) and writes it back immediately to prevent duplicate scheduling entries."""
if response.status_code == 200 and response.text.strip(): rows = []
lines = response.text.splitlines() selected_subject = None
reader = csv.DictReader(lines) 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: for row in reader:
if row.get('Used', '').lower() == 'no': # Find the first unused item matching criteria
return { 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', f"https://github.com/search?q={row['Title']}")
}
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'], "title": row['Title'],
"desc": row['Description'], "desc": row['Description'],
"points": [row['Point1'], row['Point2'], row['Point3']], "points": [row['Point1'], row['Point2'], row['Point3']],
@ -60,62 +86,76 @@ def fetch_subject():
except Exception: except Exception:
pass pass
# Fallback Logic: Scrape GitHub Trending AI/LLM Python/Go repositories # 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: try:
fb_res = requests.get("https://github.com/trending", timeout=10) fb_res = requests.get("https://github.com/trending", timeout=10)
soup = BeautifulSoup(fb_res.text, 'html.parser') soup = BeautifulSoup(fb_res.text, 'html.parser')
repo = soup.find('article', class_='Box-row') repo = soup.find('article', class_='Box-row')
title = repo.find('h2', class_='h3').text.strip().replace('\n', '').replace(' ', '') raw_title = repo.find('h2', class_='h3').text.strip().replace('\n', '').replace(' ', '')
desc = repo.find('p', class_='col-9').text.strip() desc = repo.find('p', class_='col-9').text.strip()
return { return {
"title": title, "title": raw_title.split('/')[-1],
"desc": desc, "desc": desc,
"points": ["Open-source community standard", "Highly scalable architecture", "Streamlined local environment deployment"], "points": ["Open-source workflow standard", "Highly modular architectural design", "Streamlined environment footprint"],
"url": f"https://github.com/{title}" "url": f"https://github.com/{raw_title}"
} }
except Exception: except Exception:
# Absolute hard-fail recovery state
return { return {
"title": "Model Context Protocol", "title": "Model Context Protocol",
"desc": "An open standard enabling secure, structured data communication layers between local environments and LLMs.", "desc": "An open standard enabling secure, structured data communication layers between local environments and generative applications.",
"points": ["Eliminates custom integration glue code", "Maintains clean architectural boundaries", "Built for developer tool interoperability"], "points": ["Eliminates custom integration glue code", "Maintains clear architectural boundaries", "Built for cross-tool interoperability"],
"url": "https://github.com/modelcontextprotocol" "url": "https://github.com/modelcontextprotocol"
} }
def generate_assets(): def generate_assets():
data = fetch_subject() data = fetch_and_update_subject()
title = clean_typography(data['title']) title = clean_typography(data['title'])
desc = clean_typography(data['desc']) desc = clean_typography(data['desc'])
points = [clean_typography(p) for p in data['points']] 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 = CodeCanvasPDF()
pdf.set_margins(15, 30, 15) pdf.set_margins(15, 30, 15)
pdf.add_page() pdf.add_page()
# Title Layout (High-contrast Serif Display Style) # Header display layout
pdf.set_y(40) pdf.set_y(40)
pdf.set_text_color(*COLOR_NAVY) pdf.set_text_color(*COLOR_NAVY)
pdf.set_font("Times", "", 28) # Georgia alternative pdf.set_font("Times", "", 26)
pdf.multi_cell(180, 12, title) pdf.multi_cell(180, 12, title)
# Accent Divider Line # Layout dividing grid rules
pdf.set_draw_color(*COLOR_BORDER) pdf.set_draw_color(*COLOR_BORDER)
pdf.line(15, pdf.get_y() + 5, 80, pdf.get_y() + 5) 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_y(pdf.get_y() + 15)
pdf.set_text_color(*COLOR_CHARCOAL) 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) pdf.multi_cell(180, 7, desc)
# Structured Grid for Key Highlights # Balanced spacing grids for parameters
pdf.set_y(pdf.get_y() + 15) pdf.set_y(pdf.get_y() + 15)
for idx, pt in enumerate(points, 1): for idx, pt in enumerate(points, 1):
pdf.set_draw_color(*COLOR_BORDER) pdf.set_draw_color(*COLOR_BORDER)
pdf.set_fill_color(*COLOR_LINEN) pdf.set_fill_color(*COLOR_LINEN)
# Structural thin-bordered card layouts
pdf.rect(15, pdf.get_y(), 180, 18, 'DF') pdf.rect(15, pdf.get_y(), 180, 18, 'DF')
pdf.set_x(20) pdf.set_x(20)
@ -128,11 +168,11 @@ def generate_assets():
pdf.cell(160, 18, pt) pdf.cell(160, 18, pt)
pdf.set_y(pdf.get_y() + 22) pdf.set_y(pdf.get_y() + 22)
pdf_filename = "linkedin_insight_asset.pdf" pdf_path = f"_drafts/{safe_date}.pdf"
pdf.output(pdf_filename) pdf.output(pdf_path)
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# 2. GENERATE LINKEDIN POST COPY # MARKDOWN CONVERSION BLOCK
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
post_copy = f"""💡 Weekly AI Architecture Breakdown post_copy = f"""💡 Weekly AI Architecture Breakdown
@ -152,10 +192,11 @@ Repository Link: {data['url']}
#SoftwareArchitecture #DeveloperTools #TechInsights #CodeAndCanvas""" #SoftwareArchitecture #DeveloperTools #TechInsights #CodeAndCanvas"""
print("--- LINKEDIN POST DRAFT ---") md_path = f"_drafts/{safe_date}.md"
print(post_copy) with open(md_path, mode='w', encoding='utf-8') as f:
print("\n--- ASSET STATUS ---") f.write(post_copy)
print(f"Success: {pdf_filename} generated inside your directory following brand design guidelines.")
print(f"Workflow Complete: Created tracking drafts inside configuration paths.")
if __name__ == "__main__": if __name__ == "__main__":
generate_assets() generate_assets()