AI Article Generator

Overview of the AI-Driven Content Generation and Management System

This software system is an advanced, AI-powered platform designed for automated content creation and digital publishing. Utilizing Python and various APIs, it integrates artificial intelligence, image processing, and web services to automate the generation, editing, and distribution of digital content, especially articles.

Click Here to View my AI Article Generating Website
from flask import Flask, request, jsonify, make_response
from flask_cors import CORS
from contentgenerator import GenerateArticle


app = Flask(__name__)

# Enable CORS for all domains on all routes
CORS(app, resources={r"/*": {"origins": "*"}}, supports_credentials=True)

@app.route('/generate_article', methods=['POST', 'OPTIONS'])
def generate_article():
    
    if request.method == 'OPTIONS':
        response = make_response()
        response.headers.set('Access-Control-Allow-Origin', '*')
        response.headers.set('Access-Control-Allow-Headers', 'Content-Type')
        response.headers.set('Access-Control-Allow-Methods', 'POST')
        print('OPTIONS Response Headers:', response.headers)
        return response
    else:
        try:
            data = request.json
            generator = GenerateArticle()
            article, post_id, post_url = generator.generate_article(data['topic'])


            # If article is None or ValueError is raised
            if article is None or post_id is None:
                return jsonify({'error': 'Article generation failed'}), 400

            return jsonify({'post_id': post_id, 'post_url': post_url})

        except ValueError as e:
            return jsonify({'error': str(e)}), 400

if __name__ == '__main__':
    app.run(debug=True, port=5000)

main.py

  1. Flask Application Setup:
    • Initializes a Flask app instance, setting up the web server backend.
    • Enables Cross-Origin Resource Sharing (CORS) across all routes for unrestricted access from different domains, crucial for API accessibility.
  2. Article Generation Endpoint (/generate_article):
    • Defines a POST route to handle article generation requests.
    • Uses an OPTIONS method to respond to preflight requests in CORS contexts, ensuring compatibility with browsers’ same-origin policy.
    • Extracts the article topic from the incoming JSON request.
    • Creates an instance of GenerateArticle from contentgenerator.py to generate the article, post ID, and URL based on the provided topic.
    • Handles potential errors with appropriate JSON responses, indicating robust error handling.
  3. Response Handling:
    • Returns a JSON response containing the post ID and URL if the article generation is successful.
    • Sends an error response if the article generation fails or encounters exceptions, ensuring the client receives meaningful feedback.
  4. Server Execution:
    • Configures the Flask app to run with debug mode enabled on port 5000, facilitating development and testing.
from email.mime import image
from nntplib import ArticleInfo
from pydoc_data.topics import topics
from urllib import request
from wordpress import wordPress
from config import UNSPLASH_API_KEY, WORDPRESS_PASSWORD, WORDPRESS_USERNAME,WORDPRESS_SITE_URL, GOOGLE_API_KEY, GOOGLE_CX, OPENAI_API_KEY
import requests
import json
import random
import re
import pandas as pd
from aiassistant import OpenAIAssistant

class Images:
    def __init__(self,openai_assistant):
        self.currentarticle = OpenAIAssistant()
        self.WpArticle = wordPress()
        self.offset = 0
        
        #self.google_api_key = google_api_key

        #self.google_cx = google_cx
        #self.unsplash_api_key = unsplash_api_key
    def search_google_images(query):

        url = "https://www.googleapis.com/customsearch/v1"
        params = {
            'key': GOOGLE_API_KEY,
            'cx': GOOGLE_CX,
            'q': query,
            'searchType': 'image',
            'imgSize': 'large',  # Set image size to large for higher quality images
            'num': 10,
        } # Number of results per request        }
        response = requests.get(url, params=params)
        if response.status_code == 200:
            results = json.loads(response.content)['items']
            if results:
                return random.choice(results)['link']
        return None    # Define a function to search for images on Unsplash based on the topic
    def search_unsplash_images(self,topics):
        headers = {
            'Authorization': f'Client-ID {UNSPLASH_API_KEY}',

            'Accept-Version': 'v1'
        }
        # Initialize empty lists to store featured image URLs and content image URLs
        featured_image_urls = []
        content_image_urls = []
        for topic in topics:
            # Search for an image for the featured image
            query = topic            
            url = f'https://api.unsplash.com/search/photos?query={query}&orientation=landscape'
            response = requests.get(url, headers=headers)

            if response.status_code == 200:
                results = json.loads(response.content)['results']

                if results:
                    # Select a random image from the results
                    featured_image_url = random.choice(results)['urls']['regular']
                    featured_image_urls.append(featured_image_url)
            else:

                featured_image_urls.append(None)
            # Search for an image to add to the content
            query = f'{topic}'
            url = f'https://api.unsplash.com/search/photos?query={query}&orientation=landscape'

            response = requests.get(url, headers=headers)
            if response.status_code == 200:
                results = json.loads(response.content)['results']

                if results:
                    # Select a random image from the results
                    content_image_url = random.choice(results)['urls']['regular']

                    content_image_urls.append(content_image_url)
                else:
                    content_image_urls.append(None)
            else:
                content_image_urls.append(None)
        return random.choice(featured_image_urls), random.choice(content_image_urls)
    def image_info_dictionary(self, article):              
        image_positions = {}
        # Adjusted regex pattern to match your image placeholders and descriptions
        image_pattern = r'\["?image\s*(\d+)"?\]\s*\["?([^"]+)"?\]'
        
        matches = list(re.finditer(image_pattern, article))
        print("Number of matches found:", len(matches))  # Debug print
        for match in matches:
            image_placeholder = match.group(1)
            image_description = match.group(2)
            position = match.start()

            image_positions[image_placeholder] = {
                "pos": position,
                "image_description": image_description,
                "matched_string": match.group(0)
            }
            print(f"Match: {match.group(0)}, Position: {position}, Description: {image_description}")  # Debug print

        return image_positions, article

    def insert_content_images(self, article, topic):
        print("Article before processing:", article)  # Debug print

        image_dictionary, updated_article = self.image_info_dictionary(article)

        print("Image Dictionary:", image_dictionary)  # Debug print

        for image_info in image_dictionary.values():
            pos = image_info["pos"] + self.offset
            matched_string = image_info["matched_string"]
            description = image_info["image_description"]

            # Debug prints
            print("Position:", pos)
            print("Description:", description)

            # Generate the image using the description
            image_url = self.currentarticle.generate_image(description)
            print("Generated Image URL:", image_url)

            # Insert the image URL into the article text at the specified position
            content_media_id, wp_content_image_url = self.WpArticle.upload_media_to_wordpress(image_url, topic)
            updated_article = updated_article[:pos] + f'<img src="{wp_content_image_url}">' + updated_article[pos + len(matched_string):]
            self.offset += len(f'<img src="{wp_content_image_url}">') - len(matched_string)

            print("Updated Article Segment:", updated_article[pos:pos+100])  # Print a segment of the updated article for verification

        return updated_article

    def insert_featured_images(self,img_keywords,topic):
        featured_image_url = self.currentarticle.generate_image(img_keywords)        
        #featured_image_url, content_image_url_notused = self.search_unsplash_images(img_keywords)
        #image_description = self.generate_image_description(article)
        #content_image_url=self.article.generate_image(image_description)
        featured_media_id, featured_image_url = self.WpArticle.upload_media_to_wordpress(featured_image_url, topic)
        return featured_media_id, featured_image_url


Image_handling.py

Key Functionalities:

  1. Image Searching:
    • Implements functions to search for images using Google’s Custom Search API (search_google_images) and Unsplash API (search_unsplash_images). This showcases the script’s capability to integrate with external APIs and retrieve relevant images based on given topics or queries.
  2. Image Information Processing:
    • The image_info_dictionary method processes articles to identify placeholders for images and their descriptions. It uses regex patterns to match and extract image-related information, demonstrating proficiency in text processing and pattern matching.
  3. Image Integration into Articles:
    • The insert_content_images method incorporates images into articles at specified positions. It generates images using descriptions through the OpenAIAssistant, uploads them to WordPress, and updates the article content with image URLs. This process highlights the script’s ability to manage content dynamically and integrate AI-generated images into textual content.
  4. Featured Image Handling:
    • The insert_featured_images method handles the generation and uploading of featured images for articles, further integrating with WordPress functionalities. This method signifies the script’s role in enhancing the visual appeal and completeness of generated articles.

Complexities:

  • API Integration and Usage: The script demonstrates complex interactions with external APIs (Google, Unsplash, WordPress) for image searching and uploading. Handling API responses, especially in the context of image data, requires careful parsing and error handling.
  • Regular Expressions: Utilizing regex for parsing image placeholders and descriptions in articles indicates an advanced understanding of text processing, crucial for dynamic content manipulation.
  • Dynamic Content Modification: The ability to dynamically insert images into articles based on AI-generated descriptions and user-defined placeholders shows a sophisticated approach to content management.
  • Error Handling and Debugging: The script includes error handling mechanisms and debugging prints, essential for ensuring robustness and easing maintenance and troubleshooting.
import pandas as pd
from wordpress import wordPress
from aiassistant import OpenAIAssistant
from image_handling import Images
from gui import CustomDialog, show_article_dialog
import openpyxl
import json
import utils
from config import DATABASE_LOCATION, ARTICLE_DATABASE, ARTICLES

class GenerateArticle:

    def __init__(self):
        self.article = OpenAIAssistant()
        self.images = Images(self.article)
        self.wpArticle = wordPress()
    def generate_image_description(self, article_text):
        prompt = f"Based on the following article, provide a short description for a relevant image:\n\n{article_text}"
        return self.article.ask_gpt(prompt)
    def generate_topics(self, n_topics=1):
        topics = []
        for _ in range(n_topics):
            response = self.article.ask_gpt(self._topic_prompt())
            response_dict, error = utils.parse_json_string(utils.clean_response_string(response))
            if error:
                # Handle the error appropriately, e.g., log the error or raise an exception
                continue
            utils.append_data_to_excel(pd.DataFrame(response_dict), ARTICLES)
            topics.append(response_dict)
        return topics

    def generate_initial_article(self, topic):
        article = self.article.ask_gpt(self._article_prompt(topic))
        article = self._clean_article(article)
        title = self._generate_title(topic)
        return article, title

    def generate_improved_article(self, topic):

        prompt = f"Improve journal article on {topic}"
        improved_article = self.article.ask_gpt(prompt)
        title = self._generate_title(improved_article)
        return improved_article, title

    def edit_article(self, user_input, article):
        return self.article.ask_gpt(f"{user_input}\n{article}")

    """    def generate_article(self, img_keywords, topic, category):
        article, title = self.generate_initial_article(topic)
        user_decision, article = self._article_dialog_loop(article)

        if user_decision == "Upload Article":
            article = self.images.insert_content_images(article, topic)
            featured_media_id, _ = self.images.insert_featured_images(img_keywords, topic)
            category_id = self.wpArticle.get_category_id(category)
            if category_id:
                PostID = self.wpArticle.create_post(title, article, category_id, featured_media_id)
                return article, PostID
        elif user_decision == "Do Not Upload":
            # Skip to the next topic or handle as needed
            return None, None
        # If user decides to edit the output, no action is needed here
        return article, None"""

    def generate_image_keywords(self, topic):
            prompt = f"""Provide an image description for Dalle to generate an interesting image on the topic '{topic}' in a python list format like: ["Keyword1","Keyword2","Keyword3"] """
            response = self.article.ask_gpt(prompt)            
            try:
                # Parsing the string response into a Python list
                image_keywords_list = eval(response)  # Use eval() cautiously
                print(image_keywords_list)
                
                # Joining the list into a comma-separated string
                image_keywords = ', '.join(image_keywords_list)
                print(image_keywords)
                return image_keywords
            except Exception as e:
                print(f"Could not generate image keywords: {e}")
                # Handle the error appropriately
                return []
            
                """ def generate_image_keywords(self, topic):
                prompt = "Provide an image description for Dalle to generate an interesting image on the topic '{topic}' in a python list format like: ["Keyword1","Keyword2","Keyword3"] 
                response = self.article.ask_gpt(prompt)            
                try:
                # Parsing the string response into a Python list
                image_keywords_list = eval(response)  # Use eval() cautiously
                print(image_keywords_list)
                
                # Joining the list into a comma-separated string
                image_keywords = ', '.join(image_keywords_list)
                print(image_keywords)
                return image_keywords
                except Exception as e:
                print(f"Could not generate image keywords: {e}")
                # Handle the error appropriately
                return []"""
                
    def generate_image_keywords(self, topic):
            prompt = f"""Provide an a short one sentence image description for Dalle to generate an interesting image on the topic '{topic}' in a python list format like: ["an intersting desciription for featured image of article"] """
            response = self.article.ask_gpt(prompt)            
            try:
                # Parsing the string response into a Python list
                image_keywords_list = eval(response)  # Use eval() cautiously
                print(image_keywords_list)
                
                # Joining the list into a comma-separated string
                image_keywords = ', '.join(image_keywords_list)
                print(image_keywords)
                return image_keywords
            except Exception as e:
                print(f"Could not generate image keywords: {e}")
                # Handle the error appropriately
                return []

    def generate_category(self, topic):
        prompt = f"Provide one word for an appropriate category for a website post on the topic: '{topic}' in python list format such as: [category]"
        response = self.article.ask_gpt(prompt)
        print(response)
        # Attempt to parse the response
        try:
            # Removing brackets and quotes, and trimming whitespace
            cleaned_response = response.replace('[', '').replace(']', '').replace("'", "").replace('"', '').strip()
            # Splitting the string into a list based on commas
            category_list = cleaned_response.split(',')
            # Get the first item in the list as the category, or default to "Uncategorized"
            category = category_list[0].strip() if category_list else "Uncategorized"
            return category
        except Exception as e:
            print(f"An error occurred: {e}")
            # Handle the error appropriately
            return "Uncategorized"

    def generate_article(self, topic):
        image_keywords = self.generate_image_keywords(topic)
        category = self.generate_category(topic)
        article, title = self.generate_initial_article(topic)
        # Check if the article is less than 50 words
        word_count = len(article.split())
        if word_count < 50:
            raise ValueError("Generated article is too short.")
        
        # Insert images into the article
        article = self.images.insert_content_images(article, topic)
        featured_media_id, _ = self.images.insert_featured_images(image_keywords, topic)

        # Create the WordPress post
        category_id = self.wpArticle.get_category_id(category)
        if category_id:
            post_id, post_url = self.wpArticle.create_post(title, article, category_id, featured_media_id)
            return article, post_id, post_url
        else:
            return None, None

    def _article_dialog_loop(self, article):
        user_decision = None
        user_input = None
        while True:
            button_clicked, user_input = show_article_dialog(self.root, article)

            if button_clicked == "Upload Article":
                user_decision = "Upload Article"
                break
            elif button_clicked == "Do Not Upload":
                user_decision = "Do Not Upload"
                break
            elif button_clicked == "Edit Output":
                article = self.article.ask_gpt(user_input + "\n" + article)
                # Continue the loop for further actions

        return user_decision, article

    def _topic_prompt(self):
        topic = """
            create a nested dictionary of interesting topics for articles, 
            keywords for images to represent that article, and a 
            category for that article in the format shown below:
            topic_dict = {
                "topic1": {
                    "Topic": "interesting topic",
                    "Image Search": [keyword1, keyword2, keyword3],
                    "Category": "website category"
                },
                "topic2": {
                    "Topic": "interesting topic",
                    "Image Search": [keyword1, keyword2, keyword3],
                    "Category": "website category"
                },
                "topic3": {
                    "Topic": "interesting topic",
                    "Image Search": [keyword1, keyword2, keyword3],
                    "Category": "website category"
                }
            }

            please don't write anything else but the dictionary
            """
        return topic

    def _article_prompt(self,articleprompt):
        prompt = f"""Generate an interesting and informative article between 200 and
        500 words on the topic: '{articleprompt}'. Make sure the article does not
        have a title, does not have an abstract, and does not have a heading 
        for the introduction. Enclose subheadings with HTML: <h5> tag. Include paragraph tags: <p> tag. Create 2 image positions 
        and descriptions after the closing paragraph tags (</p>) in the article .
        The positions should be in the form "image1","image2" etc. followed by image description as shown: ["image1"][image 1 description].
        The images should be relevant to the paragraph they are inserted into. The description should be a short sentence that describes the image.
        The description will be used with an AI like DALLE to generate an image relevant
        to the corresponding paragraph."""
        return prompt

    def _generate_title(self, topic):
        prompt = f"""Write me an interesting title for the {topic} in the form: ["title"]"""
        response = self.article.ask_gpt(prompt)
        try:
            # Removing brackets and quotes, and trimming whitespace
            cleaned_response = response.replace('[', '').replace(']', '').replace("'", "").replace('"', '').strip()
            # Splitting the string into a list based on commas
            title_list = cleaned_response.split(',')
            # Get the first item in the list as the category, or default to "Uncategorized"
            title = title_list[0].strip() if title_list else "No Title"
            return title
        except Exception as e:
            print(f"An error occurred: {e}")
            # Handle the error appropriately
            return "No Title"
        
    def _clean_article(self, article):
        # Add your cleaning logic here
        cleaned_article = article  # Replace this with actual cleaning operations
        return cleaned_article


class GenerateArticleTopics:
    def __init__(self, root):
        self.root = root
        self.topic_ai = OpenAIAssistant()
    
    def generate_topics(self, n_topics=1):
        topics = []
        for _ in range(n_topics):
            response = self.topic_ai.ask_gpt(self._topic_prompt())
            response_dict, error = utils.parse_json_string(utils.clean_response_string(response))

            if error:
                # Handle the error appropriately
                continue
            topics.append(response_dict)
            utils.append_data_to_excel(pd.DataFrame(response_dict), "ArticleTypes.xlsx")
        return topics    
    def process_topics(self):
        workbook, worksheet = utils.load_or_initialize_workbook(DATABASE_LOCATION + ARTICLES, ["Image Search", "Topic", "Category"])
        database_workbook, database_worksheet = utils.load_or_initialize_workbook(DATABASE_LOCATION + ARTICLE_DATABASE, ["Image Search", "Topic", "Category"])
        
        topics = utils.read_worksheet(worksheet)
        database_topics = utils.read_worksheet(database_worksheet)
        
        matching_topics = set(topics).intersection(database_topics)
        utils.delete_matching_rows(worksheet, matching_topics)
        utils.save_workbook(workbook, DATABASE_LOCATION + ARTICLES)
        
        for topic_data in utils.iterate_rows(worksheet, ["Image Search", "Topic", "Category"]):
            image_search, topic, category = topic_data
            print(topic_data["Image Search"])
            if topic_data["Topic"] not in matching_topics:
                self._generate_and_process_article(topic_data["Image Search"],topic_data["Topic"],topic_data["Category"], database_worksheet)
        
        utils.save_workbook(database_workbook, DATABASE_LOCATION + ARTICLE_DATABASE)
        
        utils.save_workbook(database_workbook, "ArticleTypes_Database.xlsx")

    def _topic_prompt(self):
        return("""
            create a nested dictionary of interesting topics for articles, 
            keywords for images to represent that article, and a 
            category for that article in the format shown below:
            topic_dict = {
                "topic1": {
                    "Topic": "interesting topic",
                    "Image Search": [keyword1, keyword2, keyword3],
                    "Category": "website category"
                },
                "topic2": {
                    "Topic": "interesting topic",
                    "Image Search": [keyword1, keyword2, keyword3],
                    "Category": "website category"
                },
                "topic3": {
                    "Topic": "interesting topic",
                    "Image Search": [keyword1, keyword2, keyword3],
                    "Category": "website category"
                }
            }
            please don't write anything else but the dictionary
            """)

    def _generate_and_process_article(self, image_keywords, topic, category, database_worksheet):
        article_generator = GenerateArticle(self.root)
        article, PostID = article_generator.generate_article(image_keywords, topic, category)
        if PostID:
            database_worksheet.append([', '.join(image_keywords), topic, category])
            print(f"Article for topic '{topic}' has been generated and posted with ID {PostID}.")


contentgenerator.py

Key Functionalities:

  1. AI-Driven Content Generation:
    • Initial Article Generation: Uses OpenAI’s GPT model to generate a draft of the article based on a given topic, demonstrating the integration of AI for initial content creation.
    • Improved Article Generation: Enhances the quality of the initial draft by generating a refined version, showcasing advanced use of AI for content improvement.
  2. Image Description and Keyword Generation:
    • Generates image descriptions and keywords relevant to the article’s topic using AI, which are essential for sourcing and creating appropriate images for the articles.
  3. Category Generation:
    • Automatically generates a category for the article using AI, aiding in the organized classification of the content on WordPress.
  4. Image Integration:
    • Integrates the Images class from image_handling.py to insert AI-generated or sourced images into the article at appropriate positions, enhancing the article’s visual appeal.
  5. WordPress Article Creation:
    • Creates and uploads the final article to WordPress, complete with AI-generated content, images, and category, using the wordPress class from wordpress.py.
  6. Interactive Article Editing (_article_dialog_loop):
    • Provides an option for manual editing of the article through a GUI, allowing for user intervention and customization before finalizing the content.
  7. Topic and Article Data Management:
    • Manages topics and article data using Excel workbooks, storing and processing information necessary for article generation and tracking.

Complexities:

  • AI Integration and Text Processing: The script exhibits complex integration with OpenAI’s GPT model, processing and utilizing AI-generated text for various aspects of article creation.
  • Modular Design and External Class Integration: Demonstrates a modular approach by integrating functionalities from image_handling.py and wordpress.py, indicating a well-structured and maintainable codebase.
  • GUI for User Interaction: Incorporates user interfaces for article editing, showcasing the ability to blend automated processes with manual interventions.
  • Data Management: Manages data effectively using Excel workbooks, displaying proficiency in handling and organizing large sets of data.
import tkinter as tk
from tkinter import simpledialog

class CustomDialog(simpledialog.Dialog):
    def __init__(self, parent, title, message, buttons, article):
        self._buttons = buttons
        self.article = article
        self.message = message
        self.result = None  # Initialize result
        super().__init__(parent, title=title)

    def body(self, frame):
        tk.Label(frame, text=self.message, padx=10, pady=10).pack()
        self.text_widget = tk.Text(frame, padx=10, pady=10, wrap='word', width=50, height=10)
        self.text_widget.insert(tk.END, self.article)
        self.text_widget.pack(side='left', fill='both', expand=True)

        self.user_input = tk.Entry(frame)
        self.user_input.pack(pady=10)

        scrollbar = tk.Scrollbar(frame, command=self.text_widget.yview)
        scrollbar.pack(side='right', fill='y')
        self.text_widget.config(yscrollcommand=scrollbar.set)

        for button_text in self._buttons:
            button = tk.Button(frame, text=button_text, command=lambda text=button_text: self.button_click(text))
            button.pack(side=tk.LEFT, padx=10, pady=10)

        return self.user_input

    def button_click(self, text):
        if text == "Upload Article":
            self.article = self.text_widget.get("1.0", tk.END).strip()
        self.result = (text, self.user_input.get())
        self.destroy()

    
def show_article_dialog(root, article):
    message = "Do you want to upload this article to WordPress?"
    buttons = ["Upload Article", "Do Not Upload", "Edit Output"]
    dialog = CustomDialog(root, "Upload Article?", message, buttons, article)

    #root.wait_window(dialog)  # Wait for the dialog to close

    if hasattr(dialog, 'result'):
        return dialog.result
    return None, None  # Return a default value if dialog was closed unexpectedly

gui.py

Key Functionalities:

  1. Custom Dialog Creation:
    • The CustomDialog class extends the simpledialog.Dialog of the Tkinter library, providing a customizable modal dialog window. This class is pivotal for creating interactive dialog boxes that capture user inputs and decisions.
  2. Article Display and Editing Interface:
    • The dialog created by CustomDialog includes a text widget for displaying the generated article, allowing users to read and optionally edit the content. This feature is crucial for ensuring that users can review and modify AI-generated articles before they are published.
  3. User Decision Handling:
    • The GUI offers buttons (such as “Upload Article”, “Do Not Upload”, “Edit Output”) for users to make decisions regarding the article. The script captures these decisions and the potential edits made to the article, illustrating how user feedback is integrated into the content generation process.
  4. Show Article Dialog Function:
    • The show_article_dialog function initiates the dialog with the article content and captures the user’s decision and input. This function acts as a bridge between the article generation logic and the GUI, facilitating user interaction.

Complexities:

  • Tkinter GUI Development: Utilizing Tkinter to develop a custom dialog demonstrates an understanding of Python’s standard GUI toolkit. It involves handling window events, user inputs, and interface layout, which are fundamental aspects of desktop application development.
  • Integration with Content Generation System: The script is designed to integrate seamlessly with the content generation workflow. It interacts with other components of the system, such as the article generation and processing modules, to obtain and return user-modified content.
  • User Interaction and Feedback Incorporation: The ability to capture and process user decisions and inputs for AI-generated content highlights the system’s flexibility and responsiveness to user feedback.
import requests
import base64
import json
from io import BytesIO
from PIL import Image
from config import WORDPRESS_API_PASSWORD, WORDPRESS_PASSWORD, WORDPRESS_EMAIL, WORDPRESS_SITE_URL, WORDPRESS_USERNAME


#Word Press Integration Class
class wordPress:
    def __init__(self):
        self.username = WORDPRESS_USERNAME  # Replace with your WordPress email
        self.password = WORDPRESS_PASSWORD           # Replace with your WordPress password
        self.site_url = WORDPRESS_SITE_URL # Replace with your WordPress site URL
        self.credentials = f"{self.username}:{self.password}"
        self.token = base64.b64encode(self.credentials.encode())
        self.headers = {"Authorization": f"Basic {self.token.decode('utf-8')}"}

    def category_exists(self, category_name):
        # Check if the category already exists
        url = f'{WORDPRESS_SITE_URL}/wp-json/wp/v2/categories?search={category_name}'
        response = requests.get(url, headers=self.headers)
        if response.status_code == 200:
            categories = json.loads(response.content)
            if categories:
                return categories[0]['id']
        return None
    def create_category(self,category_name):
        # Create a new category
        url = f'{WORDPRESS_SITE_URL}/wp-json/wp/v2/categories'
        data = {
            'name': category_name        }
        response = requests.post(url, headers=self.headers, json=data)
        if response.status_code == 201:
            category_id = json.loads(response.content)['id']
            print(f'Category created with ID: {category_id}')
            return category_id
        return None

    def get_category_id(self,category_name):
        # Get the ID of the category
        category_id = self.category_exists(category_name)
        if category_id:
            return category_id
        else:
            return self.create_category(category_name)
        
    def upload_media_to_wordpress(self,image_url, alt_text):
        # Download the image from the URL
        response = requests.get(image_url)
        img = Image.open(BytesIO(response.content))
        # Convert the image to JPEG format
        buffer = BytesIO()
        img.save(buffer, format="JPEG")
        # Upload the image to WordPress
        data = {
            "alt_text": alt_text,
            "media_type": "image/jpeg",
        }
        files = {'file': ('image.jpg', buffer.getvalue(), 'image/jpeg', {'Expires': '0'})}
        response = requests.post(f'{WORDPRESS_SITE_URL}/wp-json/wp/v2/media', headers=self.headers, data=data, files=files)
        if response.status_code == 201:
            media_id = json.loads(response.content)['id']
            data = json.loads(response.text)
            print(f'Featured image updated with ID: {media_id}\n')
            #data = {'featured_media': media_id}
            return media_id, data['guid']['rendered']
        else:
            return None        
    def create_post(self, title, content, category_id, featured_media_id):
        # Create a post on the WordPress site
        url = f"{self.site_url}/wp-json/wp/v2/posts"
        data = {
            "title": title,
            "content": content,
            "status": "publish",
            "categories": [category_id],
            "featured_media": featured_media_id,
        }
        response = requests.post(url, headers=self.headers, json=data)
        if response.status_code == 201:
            print(json.loads(response.content))
            post_id = json.loads(response.content)["id"]
            post_url = json.loads(response.content)["guid"]["rendered"]
            print(f"\nPost created with ID: {post_id}")
            print(f"\nPost created with url: {post_url}")
            return post_id, post_url
        else:
            print(f"Failed to create post. Status Code: {response.status_code}")
            print(f"Response: {response.content}")
            return None
    

wordpress.py

Key Functionalities:

  1. WordPress Authentication and Initialization:
    • Initializes the WordPress integration with necessary credentials, including username and password. It prepares an authentication token for API requests, showcasing an understanding of secure API communication.
  2. Category Management:
    • Provides functions to check if a category already exists (category_exists) and to create a new category if needed (create_category). This feature demonstrates the script’s ability to interact with WordPress’s taxonomy system.
    • The get_category_id function streamlines the process of obtaining a category ID, either by finding an existing category or creating a new one, indicating efficient management of content categorization.
  3. Media Uploads to WordPress:
    • Implements upload_media_to_wordpress to upload images to the WordPress site. It handles image data processing and the intricacies of media file uploads via the WordPress REST API, highlighting the script’s capability in handling media content.
  4. WordPress Post Creation:
    • The create_post function is pivotal for creating new posts on the WordPress site. It takes essential parameters like title, content, category ID, and media ID to create a well-structured WordPress post, displaying the script’s integration with WordPress content management.

Complexities:

  • RESTful API Integration: The script deals with various aspects of WordPress’s REST API, managing categories, media, and posts. This requires handling HTTP requests, processing responses, and understanding WordPress’s API schema.
  • Image Processing and Uploads: Converting and uploading images to WordPress, especially handling image data in memory and preparing it for HTTP requests, indicates a sophisticated approach to media management.
  • Secure Authentication Handling: Managing secure authentication using base64 encoding and preparing HTTP headers for authenticated requests showcases an understanding of essential web security practices.
  • Error Handling and Response Management: The script includes checks for the status of API requests and appropriately handles different response scenarios, ensuring robust and reliable integration.
# utils.py

from turtle import pd
import json
import pandas as pd
import openpyxl
import re
def clean_response_string(response):
    # Cleans the response string from the AI to be in a suitable JSON format.
    cleaned_string = response.strip().replace('topic_dict =', '').strip()
    cleaned_string = cleaned_string.replace("```", "").strip()
    cleaned_string = cleaned_string.replace("'", '"')
    return cleaned_string
def load_or_initialize_excel(file_name, columns):
    # Loads an Excel file into a pandas DataFrame or initializes it if not found.
    try:
        existing_data = pd.read_excel(file_name)
    except FileNotFoundError:
        existing_data = pd.DataFrame(columns=columns)
    return existing_data
def append_data_to_excel(new_data, file_name):
    # Appends new data to an Excel file.
    existing_data = load_or_initialize_excel(file_name, new_data.columns)
    combined_data = pd.concat([existing_data, new_data], ignore_index=True)
    combined_data.to_excel(file_name, index=False)

def clean_article(article):
    # Cleans the article by removing placeholders or unwanted sections.
    intro_pattern = r'<h\d>\s*Introduction\s*</h\d>'
    abstract_pattern = r'<h\d>\s*Abstract\s*</h\d>(\s*<p>.*?</p>)?'
    article = re.sub(intro_pattern, '', article, flags=re.IGNORECASE)
    article = re.sub(abstract_pattern, '', article, flags=re.IGNORECASE | re.DOTALL)
    return article
def parse_json_string(json_string):
    # Parses a JSON string and returns a dictionary.
    try:
        return json.loads(json_string), None    
    except json.JSONDecodeError as e:
        return None, str(e)
def delete_rows_from_worksheet(worksheet, rows):
    # Deletes rows from an openpyxl worksheet.
    for row in sorted(rows, reverse=True):
        worksheet.delete_rows(row)

def save_workbook(workbook, file_path):
    # Saves an openpyxl workbook.
    workbook.save(file_path)

def load_or_initialize_workbook(file_path, columns):
    try:
        workbook = openpyxl.load_workbook(file_path)
        worksheet = workbook.active
    except FileNotFoundError:
        workbook = openpyxl.Workbook()
        worksheet = workbook.active
        for column in columns:
            worksheet.append([column])
        workbook.save(file_path)
    return workbook, worksheet

def read_worksheet(worksheet):
    return [row[1] for row in worksheet.iter_rows(min_row=2, values_only=True)]
def delete_matching_rows(worksheet, matching_set):
    rows_to_delete = [row for row, value in enumerate(worksheet.iter_rows(min_row=2, values_only=True), start=2) if value[1] in matching_set]
    delete_rows_from_worksheet(worksheet, rows_to_delete)
def iterate_rows(worksheet, columns):
    for row in worksheet.iter_rows(min_row=2, values_only=True):
        yield dict(zip(columns, row))


utils.py

Key Functionalities:

  1. Data Cleaning and Formatting:
    • Implements clean_response_string to transform AI response strings into a suitable JSON format, which is crucial for the proper interpretation and use of AI-generated content.
    • The clean_article function removes unwanted placeholders or sections from articles, ensuring the content is well-formatted and presentable for publication.
  2. Excel Workbook Management:
    • Functions like load_or_initialize_excel, append_data_to_excel, and save_workbook manage Excel workbooks for storing and retrieving data. These functions are vital for maintaining structured data records, such as topics, articles, and image keywords.
    • Provides capabilities to create new workbooks or load existing ones, append new data, and save changes, demonstrating proficiency in handling Excel files programmatically.
  3. Worksheet Data Processing:
    • The read_worksheet function reads data from Excel worksheets, crucial for retrieving stored information for processing.
    • delete_matching_rows and delete_rows_from_worksheet handle the removal of specific rows from worksheets, a necessary feature for maintaining data integrity and relevance.
  4. JSON Parsing:
    • parse_json_string parses JSON strings, converting them into Python dictionaries. This is essential for processing and using JSON-formatted data, especially when working with API responses or AI-generated content.
  5. Row Iteration:
    • iterate_rows provides a generator for iterating over rows in an Excel worksheet, streamlining data access and manipulation in a memory-efficient manner.

Complexities:

  • Data Manipulation and Management: Handling data cleaning, formatting, and Excel workbook operations requires a thorough understanding of data structures and file management in Python.
  • Integration with AI and Content Generation Components: These utilities are integral to the overall functionality of the system, supporting the AI-driven content generation process and data organization.
  • Error Handling and Robustness: The script includes error handling mechanisms, especially in file operations and data parsing, ensuring the system’s robustness and reliability.
  • Efficiency in Data Processing: The implementation of generators and efficient data processing techniques highlights a focus on performance, especially important when dealing with large datasets or complex operations.

More Projects