How to Automate Supply Chain Risk Reports: A Guide for Developers
If you’re a developer, especially for an automated platform company, this guide is for you. It walks you through a script we created to generate supply chain risk reports automatically. Creating these reports manually takes a lot of time and energy — something few developers have. However, you can automate the report generation process with AI and some scripting. You can find links to download the script file and related materials at the bottom of this guide.
What you’ll need to run the script
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.
You need an API key to use the Webz.io News API, and you can get one by contacting Webz.io.
- 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.
- 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.
Python — We’re using Python to automate the report creation process. You’ll need to ensure you can run Python code on your machine.
Automating supply chain risk reports: script breakdown
The script fetches news articles from the Webz.io News API. Next, the script calls the OpenAI API, using its text model to analyze the articles for content related to supply chain risk. It then creates a structured report for each article, outputting its findings into a Word document. The script uses the OpenAI image model to generate a main image for the report. The script combines all these elements into a completed supply chain risk report contained in the Word document.
Here is the detailed breakdown of the script:
Import packages and modules
First, the script imports the Python packages and modules, the OpenAI Python API library, and other necessary components.
1
2
3
4
5
6
7
8
9
10
11
12
|
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
|
Set global variable and access API keys
Next the script accesses the API keys (Webz.io and OpenAI) through the development environment. It also includes a global variable where you can set the number of articles the script will generate a report on.
1
2
3
4
|
WEBZ_API_KEY = os.getenv(“WEBZ_API_KEY”)
openai.api_key = os.getenv(“OPENAI_API_KEY”)
NUM_OF_REPORTS = 5 # Set the number of articles the script will generate a report on.
client = OpenAI()
|
Orchestrate entire process (main)
Towards the end of the script, you’ll see the “main” function. It orchestrates the entire process — from reading the news articles and generating reports for each one to creating a main report image to finalizing the Word document.
1
2
3
4
5
6
7
8
9
10
11
12
|
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()
|
Define functions
Now we define the different functions of our script:
Fetch news articles from Webz.io (def fetch_articles)
Makes a call to the Webz.io News API to get articles with content related to supply chain risk.
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
|
# 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”]) + “nn” + article[“text”], 10000),
‘link’: article[‘url’],
‘published’: article[‘published’]}
articles.append(article)
return articles
|
Remove duplicate posts (get_unique_posts_from_webz)
Removes duplicate posts in case they exist.
1
2
3
4
5
|
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
|
Look for similar strings (are_similar)
Checks if two strings are similar based on the Levenshtein ratio.
1
2
3
4
5
|
def are_similar(str1, str2, threshold=1):
“””
Check if two strings are similar based on Levenshtein ratio.
“””
return ratio(str1, str2) > threshold
|
Remove similar articles (remove_similar_strings)
Eliminates duplicate content by removing articles based on similar strings.
1
2
3
4
5
6
|
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
|
Trim string (trim_string)
Makes sure a string isn’t longer than max_length .
1
2
3
4
5
|
def trim_string(string, max_length):
if len(string) > max_length:
return string[:max_length]
else:
return string
|
Trim article title (trim_title)
Removes irrelevant text from the title. For example, if the real title includes “- CNN News” the script will remove this phrase.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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
|
Send prompt (call_gpt_completion)
Sends a prompt to the GPT-4 model and receives a response.
1
2
3
4
5
6
7
8
|
def call_gpt_completion(prompt):
return client.chat.completions.create(
model=“gpt-4-1106-preview”,
max_tokens=4096,
messages=[
{“role”: “user”, “content”: prompt},
]
)
|
Review articles and generate reports (generate_reports)
Goes through articles pulled from the Webz.io News API to determine if any contain content related to supply chain risk. If an article does mention a supply chain risk, the script generates a report for it.
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
|
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
|
Generate title (generate_title)
Creates a title for the supply chain risk report.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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
|
Generate introduction (generate_intro)
Generates an introductory paragraph for the report.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
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
|
HTML to formatted text (html_to_word)
Converts HTML content to formatted text in a Word document. It handles bold text and bullet lists.
1
2
3
4
5
6
7
8
9
10
11
|
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’)
|
Add hyperlink (add_hyperlink)
Inserts a hyperlink into a Word document paragraph.
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
|
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
|
Add title placeholder (insert_titles_in_text)
Adds a placeholder for inserting the titles.
1
2
3
4
5
6
7
8
9
10
11
|
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
|
Generate image (generate_article_image)
Generates a main image for the report using OpenAI’s DALL-E model with a specific prompt.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
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=“1024×1024”
)
image_url = response.data[0].url
except Exception as e:
print(“An error occurred generating the image:”, str(e))
return image_url
|
Download image (add_image_from_base64)
Downloads an image from a URL and adds it to a Word document.
1
2
3
4
5
6
7
8
9
|
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}”)
|
Create Word doc (create_word_doc)
Assembles the various components into a formatted Word document.
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
|
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)
|
AI + Python = a powerful automation tool
This script demonstrates an advanced use case of integrating AI-powered analysis and content generation with document automation in Python. It’s a comprehensive example of how combining various Python libraries with AI models can produce a powerful automation tool.
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”]) + “nn” + 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=“1024×1024”
)
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.