How to Automate Supply Chain Risk Reports: A Guide for Product Managers
Supply chain risks can significantly impact the production of your products. If you’re a product manager, you can use AI and news data from various sources to shape your product and prepare for potential supply chain risks. This guide shows you how to automatically generate supply chain risk reports using powerful tools from Webz.io and OpenAI. Depending on your skill sets, you may need technical help to complete the steps outlined in this guide. The steps show you how to create a Python script that puts these powerful tools together.
So, buckle up and prepare to gain actionable insights into your supply chain!
What you’ll need
- News Data — You should obtain news data from a reliable source. For this guide, we’re getting the data from the Webz.io News API. It provides structured news data feeds in 170+ languages from millions of news sites.
- OpenAI API — You’ll use OpenAI’s API to leverage the GPT-4 and DALL·E models. GPT-4 analyzes and summarizes the text from customer reviews, while DALL·E generates a main image for the report.
- Python — We’re using Python to automate the report creation process. You’ll need to ensure you can run Python code on your machine.
Set up your development environment
Setting up the environment to create automated reports requires the following:
- Get a Webz.io API key — You need an API key to use the Webz.io News API. To get a key, contact Webz.io.
- Get an OpenAI API key — You also need an API key for the OpenAI API. Create an account or sign in at OpenAI to get a key. OpenAI uses pay-per-use pricing for its language and image models. You can see the price points on the OpenAI website.
- Install Python — If you don’t already have a development environment with Python installed, you’ll need to set one up. If you’re using a Windows operating system, you can find a tutorial on how to get started using Python on Windows on the Microsoft website. Next, install the Python packages using pip, the standard package installer and manager for Python.
Create your automated supply chain risk report
Now that you’ve set up your development environment, you can move on to automating the report generation process. The below steps will let you automatically generate a detailed supply chain risk report.
Gather relevant data
Use the Webz.io News API to obtain news articles, creating a query that returns articles pertaining to supply chain risks. Use the API’s robust filters to ensure you only get high-quality, news content. The Python Levenshtein module ratio function in the script removes similar articles to ensure uniqueness.
Unlock and summarize hidden insights
Unleash the magic of OpenAI’s GPT-4 model to analyze the data gathered by the Webz.io News API. Use this powerful large language model to discover hidden supply chain risks that could impact your customers.
The GPT-4 model determines if an article explicitly discusses a supply chain risk. If the article does contain a risk, the model generates a detailed report about it in HTML format. The script has a global variable that you can use to set the number of reports you’d like generated. Make sure you’ve set up your OpenAI API key in your development environment before completing this step.
Craft a compelling report
Utilize Python’s Docx package to create a professional Word document for your supply chain risk report. The script inserts each analyzed article as a section in the document, structured with:
- Summary of the incident
- Background information
- Risk analysis
- Current responses and strategies
- Future implications
- Regulatory concerns
- Broader industry and economic context
- Similar risks
- Recommendations
Boost visual appeal
Use OpenAI’s DALL-E model to generate an impactful visual to use as the cover image for the report.
Finalize and share the report
Compile all the textual and visual elements into your report and review the contents carefully. Go through and polish the report for clarity and coherence. Once satisfied with the finalized report, share it with key stakeholders, incorporating their feedback to improve the report’s utility. We also recommend that you regularly review the accuracy and relevance of generated reports.
Have fun experimenting with Webz.io and OpenAI
You’re all set to start automating supply chain risk reports. By following this guide and leveraging tools from Webz.io and OpenAI, you’ll gain invaluable insights from news data that you can use to aid in strategic decision-making. Let the automated reports production begin!
Download the example code and report:
- The full Python script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 |
import docx import requests from openai import OpenAI import os import openai from Levenshtein import ratio from docx.shared import Pt from bs4 import BeautifulSoup import io from docx.oxml.shared import OxmlElement, qn from docx.shared import Pt from docx.enum.text import WD_ALIGN_PARAGRAPH WEBZ_API_KEY = os.getenv("WEBZ_API_KEY") openai.api_key = os.getenv("OPENAI_API_KEY") NUM_OF_REPORTS = 5 client = OpenAI() def are_similar(str1, str2, threshold=1): """ Check if two strings are similar based on Levenshtein ratio. """ return ratio(str1, str2) > threshold def remove_similar_strings(articles): unique_articles = [] for article in articles: if not any(are_similar(article['text'], existing['text'], 0.7) for existing in unique_articles): unique_articles.append(article) return unique_articles def trim_string(string, max_length): if len(string) > max_length: return string[:max_length] else: return string # Function to get news articles from Webz.io API def fetch_articles(query, api_key, total): endpoint = f"https://api.webz.io/filterWebContent?token={api_key}&format=json&q={query}&size=100&ts=0" all_posts = [] while total > 0: response = requests.get(endpoint) data = response.json() posts = data["posts"] if len(posts) == 0: break all_posts.extend(posts) total -= len(posts) if total > 0 and "next" in data: endpoint = f"https://api.webz.io{data['next']}" else: break articles = [] for article in all_posts: article = {'title': article["title"], 'text': trim_string(trim_title(article["title"]) + "\n\n" + article["text"], 10000), 'link': article['url'], 'published': article['published']} articles.append(article) return articles def trim_title(input_string): words = input_string.split() if "|" in input_string: return input_string.split("|")[0] last_dash_index = input_string.rfind("-") if last_dash_index != -1: right_of_dash = input_string[last_dash_index + 1:] right_words = right_of_dash.split() if len(right_words) <= 3 and len(words) > 10: return input_string[:last_dash_index] return input_string def add_image_from_base64(doc, image_url): response = requests.get(image_url) # Check if the request was successful if response.status_code == 200: image_stream = io.BytesIO(response.content) doc.add_picture(image_stream, width=docx.shared.Inches(6)) else: print(f"Failed to download image. Status code: {response.status_code}") def html_to_word(doc, html_content): soup = BeautifulSoup(html_content, 'html.parser') for element in soup.find_all(['b', 'ul']): if element.name == 'b': # Add bold text as a heading doc.add_paragraph(element.get_text(), style='Heading 2') elif element.name == 'ul': for item in element.find_all('li'): # Add list items doc.add_paragraph(item.get_text(), style='List Bullet') def add_hyperlink(paragraph, url, text): """ Add a hyperlink to a paragraph. """ part = paragraph.part r_id = part.relate_to(url, docx.opc.constants.RELATIONSHIP_TYPE.HYPERLINK, is_external=True) hyperlink = OxmlElement('w:hyperlink') hyperlink.set(qn('r:id'), r_id, ) new_run = OxmlElement('w:r') rPr = OxmlElement('w:rPr') u = OxmlElement('w:u') u.set(qn('w:val'), 'single') rPr.append(u) u = OxmlElement('w:u') u.set(qn('w:val'), 'single') rPr.append(u) new_run.append(rPr) new_run.text = text hyperlink.append(new_run) paragraph._p.append(hyperlink) return hyperlink def insert_titles_in_text(text, reports): # Placeholder for inserting the titles placeholder = "[]" # Extracting the titles from the reports and formatting them with new lines titles = "\n".join([report['title'] for report in reports]) # Replacing the placeholder with the titles updated_text = text.replace(placeholder, titles) return updated_text def generate_article_image(): print("Generating post image") image_url = "" try: response = client.images.generate( model="dall-e-3", prompt="Create an image for the cover of a supply chain risk report digest. The image should feature a stylized, semi-abstract illustration of a global supply chain network. Imagine a globe in the center, with lines and nodes representing international trade routes and supply nodes. Overlay this with subtle, translucent icons symbolizing various risks such as a storm cloud for natural disasters, a broken chain link for disruptions, and a padlock for cybersecurity threats. The color scheme should be professional and muted, with blues, grays, and occasional red accents to highlight risks. The overall tone should be sophisticated and informative, suitable for a corporate report. The image should be in a landscape orientation.", n=1, size="1024x1024" ) image_url = response.data[0].url except Exception as e: print("An error occurred generating the image:", str(e)) return image_url def get_unique_posts_from_webz(query): print("Fetch posts from Webz.io") articles = fetch_articles(query, WEBZ_API_KEY, 100) filtered_articles = remove_similar_strings(articles) return filtered_articles def call_gpt_completion(prompt): return client.chat.completions.create( model="gpt-4-1106-preview", max_tokens=4096, messages=[ {"role": "user", "content": prompt}, ] ) def generate_reports(filtered_articles): print("Generating Reports") reports = [] for article in filtered_articles: print(f"Creating report about: {article['title']}") prompt = f"""Carefully review the following news article between the [] brackets and determine if there is an explicit discussion about a supply chain risk from its content. The article is as follows: [ {article['text']} ] If the article explicitly mentions or clearly discusses a supply chain risk, generate a detailed report in HTML format. Use <B> tags to highlight the titles of each section and <UL> and <LI> tags for listing items. The report should include the following sections: <HTML> <B> Summary of the Incident </B> <UL> <LI> Briefly describe the specific supply chain disruption or risk highlighted in the article. This may include details such as the nature of the risk, the affected industries or companies, and the geographical locations involved.</LI> </UL> <B> Background Information </B> <UL> <LI> Provide context about the involved parties (e.g., companies, countries) and the specific supply chain elements at risk (e.g., raw materials, manufacturing, logistics). This section should also cover any relevant history or prior incidents that relate to the current risk. </LI> </UL> <B> Risk Analysis </B> <UL> <LI> Nature of the Risk: Define whether it is a logistical, geopolitical, environmental, technological, or market-related risk </LI> <LI> Impact Assessment: Evaluate the potential impact on various stakeholders, such as businesses, consumers, and economies. Consider short-term and long-term effects. </LI> <LI> Probability and Severity: Estimate the likelihood of the risk materializing and its potential severity. </LI> </UL> <B> Current Responses and Strategies </B> <UL> <LI> Measures Taken: Outline any current responses or mitigation strategies employed by affected companies or governments. </LI> <LI> Effectiveness: Assess the effectiveness of these strategies and whether they address the root causes of the risk. </LI> </UL> <B> Future Implications </B> <UL> <LI> Forecasting: Predict potential future developments and implications of the risk. </LI> <LI> Long-term Strategies: Suggest long-term strategies for companies and governments to mitigate similar risks in the future. </LI> </UL> <B> Regulatory Concerns </B> <UL> <LI> Mention any regulatory approvals and antitrust concerns, legal implications and compliance requirements.</LI> </UL> <B> Broader Industry and Economic Context </B> <UL> <LI> Discuss how this specific risk fits into wider industry trends and economic conditions.</LI> </UL> <B> Similar Risks </B> <UL> <LI> Analyze the potential for similar risks in other sectors or regions.</LI> </UL> <B> Recommendations </B> <UL> <LI> Provide actionable recommendations for stakeholders to better prepare for, mitigate, or respond to such risks. </LI> <LI> Highlight any opportunities for improvement in supply chain management or policy changes. </LI> </UL> </HTML> If the article does not explicitly mention or discuss a supply chain risk, please respond with: can't produce report. """ try: response = call_gpt_completion(prompt) report = {'text': ''} for choice in response.choices: report['text'] += choice.message.content if "Summary of the Incident" in report['text']: report['link'] = article['link'] report['title'] = article['title'] report['published'] = article['published'] reports.append(report) print(f"Created a report about: {article['title']}") else: print(f"Can't product report for: {article['title']}") if len(reports) == NUM_OF_REPORTS: break except Exception as e: print("An error occurred:", str(e)) return reports def generate_intro(reports): print("Generate post intro") prompt = """ Write a paragraph introducing a digest that contains supply-chain risk reports about the following titles, don't elaborate on these titles: [] The reports are created automatically by using Webz.io news api and ChatGPT. The reports are generated by calling the Webz.io news API for news articles about supply chain issues. The matching news articles are then run through a ChatGPT prompt to analyze if the article is indeed about a supply chain issue. If so it creates a structured report. """ prompt = insert_titles_in_text(prompt, reports) intro = "" try: response = call_gpt_completion(prompt) for choice in response.choices: intro += choice.message.content except Exception as e: print("An error occurred:", str(e)) return intro def generate_title(intro): print("Creating a title") prompt = "Create a title using the following text as a context:\n" + intro title_text = "" try: response = call_gpt_completion(prompt) for choice in response.choices: title_text += choice.message.content except Exception as e: print("An error occurred:", str(e)) title_text = title_text.strip(" ").strip('\"') if title_text.startswith("Title:"): # Sometimes ChatGPT prefix the title with Title: return title_text[len("Title:"):] return title_text def create_word_doc(file_name, title_text, image_url, intro, reports): print("Saving to word document") doc = docx.Document() # Add a title title = doc.add_paragraph() title.style = 'Title' title_run = title.add_run(title_text) title_run.font.size = Pt(24) # Set the font size title_run.font.name = 'Arial (Body)' # Set the font title.alignment = WD_ALIGN_PARAGRAPH.CENTER # Center align the title if len(image_url) > 0: add_image_from_base64(doc, image_url) doc.add_paragraph(intro) # Add each report for report in reports: p = doc.add_paragraph(style='Heading 1') add_hyperlink(p, report['link'], report['title']) doc.add_paragraph(f"Published on: {report['published']}") html_to_word(doc, report['text']) doc.add_paragraph(""" """) for paragraph in doc.paragraphs: for run in paragraph.runs: run.font.name = 'Arial (Body)' # Save the document doc.save(file_name) def main(): image_url = generate_article_image() filtered_articles = get_unique_posts_from_webz( """(sentiment:negative) AND (("supply chain" OR "fulfillment center") OR (title:(cargo OR warehouse OR shippers OR inventory OR suppliers OR fulfillment)) OR (thread.section_title:"supply chain") OR (site:*supply*))""") reports = generate_reports(filtered_articles) intro = generate_intro(reports) title_text = generate_title(intro) create_word_doc("Supply-chain risk report digest.docx", title_text, image_url, intro, reports) if __name__ == "__main__": main() |
- Example of an auto-generated report in PDF format.
To run the script:
- Ensure that Python and the required Python libraries are installed on your machine.
- Set your OpenAI API key in your development environment.
- Set your Webz.io News API key in your development environment.
- Run the script.
Ready to automate supply chain risk reports for your organization? Talk to one of our experts today.