feat: implement specialty content module with expanded Page entity fields, database migrations, and data import utilities
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
import oracledb
|
||||
from bs4 import BeautifulSoup
|
||||
import re
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
def transform_html(raw_html):
|
||||
if not raw_html:
|
||||
return "", None, None, None, None
|
||||
|
||||
soup = BeautifulSoup(raw_html, "html.parser")
|
||||
|
||||
# Extract all images
|
||||
images = []
|
||||
for img in soup.find_all('img'):
|
||||
src = img.get('src')
|
||||
if src:
|
||||
images.append(src)
|
||||
img.decompose() # Remove from flow
|
||||
|
||||
hero_image = images[0] if len(images) > 0 else "https://sisvietnam.vn/wp-content/uploads/2021/07/DSC07517-1024x576.jpg"
|
||||
other_images = images[1:]
|
||||
|
||||
# Extract Contact Info
|
||||
text = soup.get_text()
|
||||
email_match = re.search(r"(?i)Email\s*:\s*([\w\.-]+@[\w\.-]+)", text)
|
||||
phone_match = re.search(r"(?i)(?:SĐT|Điện thoại|ĐT|ĐIỆN THOẠI)\s*:\s*([\d\.\s-]+)", text)
|
||||
address_match = re.search(r"(?i)Địa chỉ\s*:\s*([^\n]+)", text)
|
||||
|
||||
contact_email = email_match.group(1).strip() if email_match else None
|
||||
contact_phone = phone_match.group(1).strip() if phone_match else None
|
||||
contact_address = address_match.group(1).strip() if address_match else None
|
||||
|
||||
# Parse sections based on wp:list or headings
|
||||
sections_data = []
|
||||
|
||||
top_uls = soup.find_all('ul', recursive=False)
|
||||
if not top_uls and soup.find('div', class_='wp-content-container'):
|
||||
top_uls = soup.find('div', class_='wp-content-container').find_all('ul', recursive=False)
|
||||
|
||||
for ul in top_uls:
|
||||
heading = "Nội dung"
|
||||
strong_tag = ul.find('strong')
|
||||
if strong_tag:
|
||||
heading = strong_tag.text.strip()
|
||||
strong_tag.decompose()
|
||||
|
||||
content_html = ""
|
||||
for li in ul.find_all('li', recursive=False):
|
||||
inner_uls = li.find_all('ul')
|
||||
if inner_uls:
|
||||
for inner in inner_uls:
|
||||
content_html += str(inner)
|
||||
else:
|
||||
content_html += str(li) # fallback to just raw text if no inner UL
|
||||
|
||||
if not content_html:
|
||||
content_html = str(ul)
|
||||
|
||||
sections_data.append({
|
||||
'heading': heading,
|
||||
'content': content_html
|
||||
})
|
||||
|
||||
if not sections_data:
|
||||
sections_data.append({
|
||||
'heading': 'Thông tin chi tiết',
|
||||
'content': str(soup)
|
||||
})
|
||||
|
||||
out = ""
|
||||
bg_colors = ["bg-white", "bg-gray-50", "bg-white", "bg-gray-100"]
|
||||
for i, sec in enumerate(sections_data):
|
||||
bg = bg_colors[i % len(bg_colors)]
|
||||
|
||||
img_html = ""
|
||||
if i < len(other_images):
|
||||
img_src = other_images[i]
|
||||
img_html = f"""
|
||||
<div class="w-full lg:w-1/2">
|
||||
<div class="rounded-2xl overflow-hidden shadow-lg aspect-[4/3] relative">
|
||||
<img src="{img_src}" class="absolute inset-0 w-full h-full object-cover">
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
flex_dir = "lg:flex-row-reverse" if i % 2 == 1 else "lg:flex-row"
|
||||
text_width = "lg:w-1/2" if img_html else "lg:w-3/4 lg:mx-auto"
|
||||
|
||||
out += f"""
|
||||
<!-- SECTION {i+1} -->
|
||||
<section class="xl:py-12 md:py-8 py-4 {bg}">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="flex flex-col gap-6 {flex_dir} lg:items-center">
|
||||
<div class="w-full {text_width}">
|
||||
<h2 class="display-6 text-primary-600 mb-4 md:mb-6 xl:mb-8">{sec['heading']}</h2>
|
||||
<div class="prose max-w-none text-gray-700 body-2" style="list-style-type: disc;">
|
||||
{sec['content']}
|
||||
</div>
|
||||
</div>
|
||||
{img_html}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
"""
|
||||
return out, hero_image, contact_email, contact_address, contact_phone
|
||||
|
||||
def main():
|
||||
print("Loading original HTML from XML...")
|
||||
file_path = "/home/x79/sisvietnamvn_01/sisvietnamvn_Trang chính thức hiện tại/post_chuyenkhoa.xml"
|
||||
tree = ET.parse(file_path)
|
||||
root = tree.getroot()
|
||||
channel = root.find("channel")
|
||||
ns = {"wp": "http://wordpress.org/export/1.2/", "content": "http://purl.org/rss/1.0/modules/content/"}
|
||||
|
||||
slug_to_raw = {}
|
||||
for item in channel.findall("item"):
|
||||
post_name = item.find("wp:post_name", ns).text
|
||||
content_node = item.find("content:encoded", ns)
|
||||
if content_node is not None and content_node.text:
|
||||
slug_to_raw[post_name] = content_node.text
|
||||
|
||||
print(f"Loaded {len(slug_to_raw)} raw posts from XML")
|
||||
|
||||
dsn = "localhost:1521/sisvietnam"
|
||||
conn = oracledb.connect(user="sisvietnam", password="sisvietnam", dsn=dsn)
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute("SELECT slug, title FROM sis_page WHERE page_type = 'CUSTOM'")
|
||||
rows = cursor.fetchall()
|
||||
|
||||
count = 0
|
||||
warnings = []
|
||||
|
||||
for row in rows:
|
||||
slug = row[0]
|
||||
title = row[1]
|
||||
|
||||
# If slug doesn't match exactly, maybe some slight difference? We'll try exact match
|
||||
if slug not in slug_to_raw:
|
||||
print(f"Slug {slug} not found in XML! Skipping.")
|
||||
continue
|
||||
|
||||
raw_html = slug_to_raw[slug]
|
||||
|
||||
# Wrap it in wp-content-container for parsing if needed
|
||||
if 'wp-content-container' not in raw_html:
|
||||
raw_html = f'<div class="wp-content-container">{raw_html}</div>'
|
||||
|
||||
transformed_content, hero_image, email, address, phone = transform_html(raw_html)
|
||||
|
||||
if "Thông tin chi tiết" in transformed_content and "wp-content-container" not in transformed_content:
|
||||
warnings.append(slug)
|
||||
|
||||
# Update DB
|
||||
update_sql = """
|
||||
UPDATE sis_page
|
||||
SET content = :1, hero_image = :2, contact_email = :3, contact_address = :4, contact_phone = :5
|
||||
WHERE slug = :6
|
||||
"""
|
||||
cursor.execute(update_sql, [transformed_content, hero_image, email, address, phone, slug])
|
||||
count += 1
|
||||
print(f"Restored and Updated {slug}")
|
||||
|
||||
conn.commit()
|
||||
print(f"Successfully fixed {count} pages.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user