From 99403e7cfe5bb9dbb92e92102fb37fdf8b0f68a0 Mon Sep 17 00:00:00 2001 From: Shane Date: Fri, 9 May 2025 09:48:35 +1000 Subject: [PATCH] fix --- foodie_utils.py | 196 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 136 insertions(+), 60 deletions(-) diff --git a/foodie_utils.py b/foodie_utils.py index 95184c4..e74793e 100644 --- a/foodie_utils.py +++ b/foodie_utils.py @@ -713,88 +713,164 @@ def get_wp_tag_id(tag_name, wp_base_url, wp_username, wp_password): def post_to_wp(post_data, category, link, author, image_url, original_source, image_source="Pixabay", uploader=None, page_url=None, interest_score=4, post_id=None, should_post_tweet=True, summary=None): """ - Post an article to WordPress and optionally tweet about it. - - Args: - post_data (dict): Contains the article title and content. - category (str): Category for the WordPress post. - link (str): Original article URL. - author (dict): Author information. - image_url (str): URL of the featured image. - original_source (str): Source of the original article. - image_source (str): Source of the image (default: "Pixabay"). - uploader (callable): Function to upload the image (optional). - page_url (str): URL of the page (optional). - interest_score (int): Score indicating article interest (default: 4). - post_id (int): ID of the post if updating (default: None). - should_post_tweet (bool): Whether to post a tweet (default: True). - summary (str): The article summary to pass to tweet generation (default: None). - - Returns: - tuple: (post_id, post_url) of the created/updated WordPress post. + Post or update content to WordPress, optionally tweeting the post. """ + import logging + import requests + import base64 + from foodie_config import X_API_CREDENTIALS + + logger = logging.getLogger(__name__) + + # Extract WordPress credentials from author dictionary + wp_url = author.get("url") + wp_username = author.get("username") + wp_password = author.get("password") + + if not all([wp_url, wp_username, wp_password]): + logger.error(f"Missing WordPress credentials for author: {wp_username or 'unknown'}") + return None, None + + # Ensure wp_url ends with '/wp-json/wp/v2' + if not wp_url.endswith('/wp-json/wp/v2'): + wp_base_url = f"{wp_url.rstrip('/')}/wp-json/wp/v2" + else: + wp_base_url = wp_url + + # Hardcoded author ID map from old working version + author_id_map = { + "owenjohnson": 10, + "javiermorales": 2, + "aishapatel": 3, + "trangnguyen": 12, + "keishareid": 13, + "lilamoreau": 7 + } + author_id = author_id_map.get(wp_username, 5) # Default to ID 5 if username not found + try: - # Prepare WordPress post data - wp_post = { - "title": post_data["title"], - "content": post_data["content"], - "categories": [category], - "status": "publish", - "featured_media": 0, # Will be updated if image is uploaded + headers = { + "Authorization": "Basic " + base64.b64encode(f"{wp_username}:{wp_password}".encode()).decode(), + "Content-Type": "application/json" } - # Handle image upload if provided - if image_url and uploader: - try: - media_id = uploader(image_url, post_data["title"]) - if media_id: - wp_post["featured_media"] = media_id - logging.info(f"Successfully uploaded image for post: {post_data['title']}") - except Exception as e: - logging.error(f"Failed to upload image for post {post_data['title']}: {str(e)}") - - # Add source attribution to content - wp_post["content"] += f"\n\n

Source: {original_source}

" + # Test authentication + auth_test = requests.get(f"{wp_base_url}/users/me", headers=headers) + auth_test.raise_for_status() + logger.info(f"Auth test passed for {wp_username}: {auth_test.json()['id']}") + + # Get or create category ID + category_id = get_wp_category_id(category, wp_base_url, wp_username, wp_password) + if not category_id: + category_id = create_wp_category(category, wp_base_url, wp_username, wp_password) + if not category_id: + logger.warning(f"Failed to get or create category '{category}', using default") + category_id = 1 # Fallback to 'Uncategorized' + else: + logger.info(f"Created new category '{category}' with ID {category_id}") + else: + logger.info(f"Found existing category '{category}' with ID {category_id}") + + # Handle tags + tags = [1] # Default tag ID (e.g., 'uncategorized') + if interest_score >= 9: + picks_tag_id = get_wp_tag_id("Picks", wp_base_url, wp_username, wp_password) + if picks_tag_id and picks_tag_id not in tags: + tags.append(picks_tag_id) + logger.info(f"Added 'Picks' tag (ID: {picks_tag_id}) due to high interest score: {interest_score}") + + # Format content with

tags + content = post_data["content"] + if content is None: + logger.error(f"Post content is None for title '{post_data['title']}' - using fallback") + content = "Content unavailable. Check the original source for details." + formatted_content = "\n".join(f"

{para}

" for para in content.split('\n') if para.strip()) + + # Upload image before posting + image_id = None if image_url: - wp_post["content"] += f"\n

Image Source: {image_source}

" - - # Simulate WordPress posting (replace with your actual WordPress API call) - # This is a placeholder; your actual implementation likely uses a WordPress API client - if post_id: # Update existing post - # Replace with actual WordPress API update call - logging.info(f"Updated WordPress post: {post_id}") - post_url = page_url if page_url else link - else: # Create new post - # Replace with actual WordPress API create call - post_id = 123 # Simulated post ID (replace with actual ID from API response) - post_url = f"https://example.com/post/{post_id}" # Simulated URL (replace with actual URL) - logging.info(f"Created WordPress post: {post_id}") + logger.info(f"Attempting image upload for '{post_data['title']}', URL: {image_url}, source: {image_source}") + image_id = upload_image_to_wp(image_url, post_data["title"], wp_base_url, wp_username, wp_password, image_source, uploader, page_url) + if not image_id: + logger.info(f"Flickr upload failed for '{post_data['title']}', falling back to Pixabay") + pixabay_query = post_data["title"][:50] + image_url, image_source, uploader, page_url = get_image(pixabay_query) + if image_url: + image_id = upload_image_to_wp(image_url, post_data["title"], wp_base_url, wp_username, wp_password, image_source, uploader, page_url) + if not image_id: + logger.warning(f"All image uploads failed for '{post_data['title']}' - posting without image") + + # Build payload + payload = { + "title": post_data["title"], + "content": formatted_content, + "status": post_data["status"], + "categories": [category_id], + "tags": tags, + "author": author_id, + "meta": { + "original_link": link, + "original_source": original_source, + "interest_score": interest_score + } + } + if image_id: + payload["featured_media"] = image_id + logger.info(f"Set featured image for post '{post_data['title']}': Media ID={image_id}") + + # Set endpoint for creating or updating post + endpoint = f"{wp_base_url}/posts/{post_id}" if post_id else f"{wp_base_url}/posts" + + logger.debug(f"Sending POST to {endpoint} with payload: {json.dumps(payload, indent=2)}") + response = requests.post(endpoint, headers=headers, json=payload) + if response.status_code != 201 and response.status_code != 200: + logger.error(f"WordPress API error: {response.status_code} - {response.text}") + response.raise_for_status() + + post_info = response.json() + if not isinstance(post_info, dict) or "id" not in post_info: + raise ValueError(f"Invalid WP response: {post_info}") + + post_id = post_info["id"] + post_url = post_info["link"] + logger.info(f"{'Updated' if post_id else 'Posted'} WordPress post: {post_data['title']} (ID: {post_id})") + + # Save to recent posts + timestamp = datetime.now(timezone.utc).isoformat() + save_post_to_recent(post_data["title"], post_url, wp_username, timestamp) # Post tweet if enabled if should_post_tweet: - credentials = X_API_CREDENTIALS.get(author["username"]) + credentials = X_API_CREDENTIALS.get(post_data["author"]) if credentials: - # Select persona for the tweet + # Select persona for the tweet (same logic as used in summarize_with_gpt4o) persona = select_best_persona(interest_score, post_data["content"]) - logging.info(f"Selected persona for tweet: {persona}") + logger.info(f"Selected persona for tweet: {persona}") # Generate GPT-based tweet tweet_post = { "title": post_data["title"], "url": post_url } - # Pass the summary to generate_article_tweet - tweet_text = generate_article_tweet(author, tweet_post, persona, summary=summary if summary else post_data["content"]) + # Use the provided summary if available, otherwise fall back to post_data["content"] + tweet_summary = summary if summary is not None else post_data["content"] + tweet_text = generate_article_tweet(author, tweet_post, persona, summary=tweet_summary) tweet_id, tweet_data = post_tweet(author, tweet_text, tweet_type="rss") if tweet_id: - logging.info(f"Successfully tweeted for post: {post_data['title']} (Tweet ID: {tweet_id})") + logger.info(f"Successfully tweeted for post: {post_data['title']} (Tweet ID: {tweet_id})") else: - logging.warning(f"Failed to tweet for post: {post_data['title']}") + logger.warning(f"Failed to tweet for post: {post_data['title']}") return post_id, post_url + except requests.exceptions.HTTPError as e: + logger.error(f"Failed to {'update' if post_id else 'post'} WordPress post: {post_data['title']}: {e} - Response: {e.response.text}", exc_info=True) + return None, None + except requests.exceptions.RequestException as e: + logger.error(f"Failed to {'update' if post_id else 'post'} WordPress post: {post_data['title']}: {e}", exc_info=True) + return None, None except Exception as e: - logging.error(f"Failed to post to WordPress for '{post_data['title']}': {str(e)}") - raise + logger.error(f"Failed to {'update' if post_id else 'post'} WordPress post: {post_data['title']}: {e}", exc_info=True) + return None, None # Configure Flickr API with credentials flickr_api.set_keys(api_key=FLICKR_API_KEY, api_secret=FLICKR_API_SECRET)