Custom Python script for updating this list
2026-04-07
#!/usr/bin/env python3
import urllib.request
import urllib.error
import getpass
import re
import io
import sys
from datetime import datetime, timezone
import xml.etree.ElementTree as ET
from ftplib import FTP_TLS
def slugify(text):
"""Convert title to a URL-friendly filename."""
text = text.lower()
text = re.sub(r'[^\w\s-]', '', text)
return re.sub(r'[-\s]+', '-', text).strip('-')
def fetch_file(url):
try:
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
with urllib.request.urlopen(req, timeout=30) as response:
return response.read().decode('utf-8')
except Exception as e:
print(f"Error fetching {url}: {e}")
return None
def generate_project_page(title, date, project_html):
"""Creates the standalone HTML. Note the 4 levels of ../ to reach root."""
return f'''<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{title} - Adriano Farina</title>
<link rel="stylesheet" type="text/css" href="../../../../shome.css" />
</head>
<body>
<article>
<nav><a href="../../../../index.html">← Back to Home</a></nav>
<header>
<h1>{title}</h1>
<p><em>{date}</em></p>
</header>
<section>
{project_html}
</section>
</article>
<footer>
<hr>
<p><a href="../../../../index.html">Adriano Farina</a></p>
</footer>
</body>
</html>'''
def add_project_to_html(html, title, date, project_html, year, subjects, project_url):
subject_classes = ' '.join(sorted(subjects))
new_entry = f'''
{date}:
{title}
{project_html}
'''
pattern = r'([\s\S]*?)(
\s*Current Projects
)'
match = re.search(pattern, html, re.IGNORECASE)
if match:
insert_pos = match.start(2)
return html[:insert_pos] + new_entry + "\n" + html[insert_pos:]
return html
def parse_rss_robustly(xml_str):
if not xml_str: return None
try:
return ET.fromstring(xml_str.strip().encode('utf-8'))
except ET.ParseError:
for fix in ["", ""]:
try:
return ET.fromstring((xml_str.strip() + fix).encode('utf-8'))
except: continue
return ET.fromstring('Adriano Farina '.encode('utf-8'))
def add_entry_to_rss(feed_xml, title, link, pub_date, description):
root = parse_rss_robustly(feed_xml)
if root is None: return None
channel = root.find('channel')
target = channel if channel is not None else root
item = ET.SubElement(target, 'item')
ET.SubElement(item, 'title').text = title
ET.SubElement(item, 'link').text = link
ET.SubElement(item, 'pubDate').text = pub_date
ET.SubElement(item, 'description').text = description
return '\n' + ET.tostring(root, encoding='unicode')
def ensure_ftp_dir(ftp, remote_directory):
"""Recursively creates directories on the FTP server."""
parts = remote_directory.split('/')
for part in parts:
if not part: continue
try:
ftp.mkd(part)
except:
pass # Directory probably exists
ftp.cwd(part)
# Go back to root after building
ftp.cwd('/')
def upload_via_ftp(host, username, password, index_html, feed_xml, project_page_html, project_dir, project_filename):
try:
ftp = FTP_TLS()
ftp.connect(host, 21, timeout=30)
ftp.auth()
ftp.prot_p()
ftp.login(username, password)
# 1. Ensure the nested directory structure exists
ensure_ftp_dir(ftp, project_dir)
# 2. Upload Main Files
ftp.storbinary('STOR index.html', io.BytesIO(index_html.encode('utf-8')))
ftp.storbinary('STOR feed.xml', io.BytesIO(feed_xml.encode('utf-8')))
# 3. Upload Project Page to its specific folder
ftp.storbinary(f'STOR {project_dir}/{project_filename}', io.BytesIO(project_page_html.encode('utf-8')))
ftp.quit()
return True
except Exception as e:
print(f"FTP Error: {e}")
return False
def main():
if len(sys.argv) >= 7:
title, date, project_html, subjects_raw, ftp_user, ftp_pass = sys.argv[1:7]
subjects = [s.strip() for s in subjects_raw.split(',')]
else:
title = input("Title: ")
date = input("Date (YYYY-MM-DD): ")
project_html = input("Description: ")
subjects = [s.strip() for s in input("Subjects: ").split(',')]
ftp_user = input("User: ")
ftp_pass = getpass.getpass("Pass: ")
# Date parsing for directories
try:
dt = datetime.strptime(date, "%Y-%m-%d")
year, month, day = date.split('-')
except:
print("Invalid date format.")
return
pub_date = datetime.now(timezone.utc).strftime("%a, %d %b %Y %H:%M:%S GMT")
# Path logic
slug = slugify(title)
project_filename = f"{slug}.html"
project_dir = f"projects/{year}/{month}/{day}"
project_path = f"{project_dir}/{project_filename}"
full_url = f"https://www.adrianofarina.it/{project_path}"
print("Fetching site...")
index_html = fetch_file("https://www.adrianofarina.it/index.html")
feed_xml = fetch_file("https://www.adrianofarina.it/feed.xml")
if not index_html: return
print("Generating files...")
project_page_content = generate_project_page(title, date, project_html)
updated_html = add_project_to_html(index_html, title, date, project_html, year, subjects, project_path)
updated_feed = add_entry_to_rss(feed_xml, title, full_url, pub_date, project_html)
print("Uploading to FTP...")
if upload_via_ftp("example.com", ftp_user, ftp_pass, updated_html, updated_feed, project_page_content, project_dir, project_filename):
print(f"SUCCESS! Page live at: {full_url}")
else:
print("Upload failed.")
if __name__ == "__main__":
main()