Skip to content

Commit b373a34

Browse files
author
Alan Christie
committed
fix: Imporved Discourse error handling
1 parent cebd030 commit b373a34

File tree

1 file changed

+70
-58
lines changed

1 file changed

+70
-58
lines changed

viewer/discourse.py

+70-58
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,41 @@
11
"""
2-
discourse.py Functions for to send and retrieve posts to/fraom the discourse platform.
2+
discourse.py Functions for to send and retrieve posts to/from the discourse platform.
3+
Discourse is not considered available if the DISCOURSE_API_KEY is not set.
34
"""
45
import logging
56
import os
67
from typing import Tuple
78

9+
import pydiscourse
810
from django.conf import settings
9-
from pydiscourse import DiscourseClient
1011

1112
from viewer.models import DiscourseCategory, DiscourseTopic
1213

1314
logger = logging.getLogger(__name__)
1415

1516

17+
def _get_client():
18+
"""Create Discourse client for the configured fragalysis user"""
19+
assert settings.DISCOURSE_API_KEY
20+
return pydiscourse.DiscourseClient(
21+
settings.DISCOURSE_HOST,
22+
api_username=settings.DISCOURSE_USER,
23+
api_key=settings.DISCOURSE_API_KEY,
24+
)
25+
26+
27+
def _get_user_client(user):
28+
"""Create Discourse client for user.
29+
The code would normally have checked that the user exists before calling this.
30+
"""
31+
assert settings.DISCOURSE_API_KEY
32+
return pydiscourse.DiscourseClient(
33+
settings.DISCOURSE_HOST,
34+
api_username=user.username,
35+
api_key=settings.DISCOURSE_API_KEY,
36+
)
37+
38+
1639
def get_user(client, username) -> Tuple[bool, str, int]:
1740
"""Call discourse API users to retrieve user by username."""
1841
logger.info('+ discourse.get_user')
@@ -67,26 +90,24 @@ def create_category(
6790

6891

6992
def process_category(category_details):
70-
"""Check category is present in Table - If not create it in Discourse and store the id returned.."""
93+
"""Check category is present in Table.
94+
If not create it in Discourse and store the id returned.
95+
"""
7196

72-
# DISCOURSE_DEV_POST_SUFFIX is used to differentiate the same target name from different dev systems in Discourse
97+
# DISCOURSE_DEV_POST_SUFFIX is used to differentiate the same target name
98+
# from different dev systems in Discourse.
7399
# It is not intended to be used for production when there is a dedicated Discourse.
74100
category_name = (
75101
category_details['category_name'] + settings.DISCOURSE_DEV_POST_SUFFIX
76102
)
77103

78-
# Create Discourse client for fragalysis user
79-
client = DiscourseClient(
80-
settings.DISCOURSE_HOST,
81-
api_username=settings.DISCOURSE_USER,
82-
api_key=settings.DISCOURSE_API_KEY,
83-
)
84-
85104
try:
86105
category = DiscourseCategory.objects.get(category_name=category_name)
87106
category_id = category.discourse_category_id
88107
post_url = os.path.join(settings.DISCOURSE_HOST, 'c', str(category_id))
89108
except DiscourseCategory.DoesNotExist:
109+
# Create Discourse client for fragalysis user
110+
client = _get_client()
90111
category_id, post_url = create_category(
91112
client,
92113
category_name,
@@ -107,49 +128,54 @@ def create_post(user, post_details, category_id=None, topic_id=None):
107128
if not user.is_authenticated:
108129
return True, 'Please logon to Post to Discourse', 0, ''
109130

110-
# Create Discourse client for user
111-
client = DiscourseClient(
112-
settings.DISCOURSE_HOST,
113-
api_username=user.username,
114-
api_key=settings.DISCOURSE_API_KEY,
115-
)
116-
117131
# Check user is present in Discourse
132+
client = _get_client()
118133
error, error_message, user_id = get_user(client, user.username)
119134
if user_id == 0:
120135
return error, error_message, 0, ''
121136

122137
title = post_details['title']
123138
content = post_details['content']
124-
tags = post_details['tags']
125-
126-
if tags is None:
127-
tags = []
139+
tags = post_details['tags'] if 'tags' in post_details else []
128140

129141
if len(content) < 20:
130142
return True, 'Content must be more than 20 characters long in Discourse', 0, ''
131143

132-
post = client.create_post(content, category_id, topic_id, title, tags)
133-
# posts url = / t / {topic_id} / {post_number}
144+
# Try to create a discourse topic post (a topic).
145+
# This might fail, especially if the topic title already exists.
146+
client = _get_user_client(user)
147+
try:
148+
post = client.create_post(content, category_id, topic_id, title, tags)
149+
except pydiscourse.exceptions.DiscourseClientError as dex:
150+
return True, dex.message, 0, ''
151+
152+
# A topic's url is {URL}/t/{topic_id}/{post_number}
134153
post_url = os.path.join(
135154
settings.DISCOURSE_HOST, 't', str(post['topic_id']), str(post['post_number'])
136155
)
137-
logger.info('- discourse.create_post')
138156

157+
logger.info('- discourse.create_post')
139158
return error, error_message, post['topic_id'], post_url
140159

141160

142161
def process_post(category_id, post_details, user):
143-
"""Check topic is present in Discourse. IF exists then post, otherwise create new topic for category"""
162+
"""Check topic is present in Discourse.
163+
If it exists then post, otherwise create new topic for category
164+
"""
165+
assert category_id
166+
assert 'title' in post_details
167+
144168
error = False
145169
error_message = ''
146170

147-
# DISCOURSE_DEV_POST_SUFFIX is used to differentiate the same target name from different dev systems in Discourse
171+
# DISCOURSE_DEV_POST_SUFFIX is used to differentiate the same target name
172+
# from different dev systems in Discourse.
148173
# It is not intended to be used for production when there is a dedicated Discourse.
149174
post_details['title'] = post_details['title'] + settings.DISCOURSE_DEV_POST_SUFFIX
150175

151-
try:
152-
topic = DiscourseTopic.objects.get(topic_title=post_details['title'])
176+
if topic := DiscourseTopic.objects.filter(
177+
topic_title=post_details['title']
178+
).first():
153179
topic_id = topic.discourse_topic_id
154180
if post_details['content'] == '':
155181
# No content - Return the URL for the topic
@@ -159,7 +185,7 @@ def process_post(category_id, post_details, user):
159185
error, error_message, _, post_url = create_post(
160186
user, post_details, topic_id=topic_id
161187
)
162-
except DiscourseTopic.DoesNotExist:
188+
else:
163189
# Create Topic for Category
164190
error, error_message, topic_id, post_url = create_post(
165191
user, post_details, category_id=category_id
@@ -170,6 +196,7 @@ def process_post(category_id, post_details, user):
170196
author=user,
171197
discourse_topic_id=topic_id,
172198
)
199+
173200
return error, error_message, topic_id, post_url
174201

175202

@@ -178,8 +205,8 @@ def topic_posts(client, topic_id):
178205
logger.info('+ discourse.topic_posts')
179206

180207
posts = client.topic_posts(topic_id)
181-
logger.info(posts)
182-
logger.info('- discourse.topic_posts')
208+
209+
logger.info('- discourse.topic_posts posts=%s', posts)
183210
return posts
184211

185212

@@ -195,8 +222,10 @@ def create_discourse_post(user, category_details=None, post_details=None):
195222
created_date=<date>, tags[list]}
196223
:return: error or url of post.
197224
"""
225+
assert user
198226

199227
logger.info('+ discourse.create_discourse_post')
228+
200229
error = False
201230
error_message = ''
202231

@@ -223,7 +252,7 @@ def create_discourse_post(user, category_details=None, post_details=None):
223252

224253

225254
def list_discourse_posts_for_topic(topic_title):
226-
"""Call discourse APIs to retreive posts for the given topic
255+
"""Call discourse APIs to retrieve posts for the given topic
227256
228257
Makes the translation from Fragalysis topic_name to discourse id.
229258
@@ -234,24 +263,15 @@ def list_discourse_posts_for_topic(topic_title):
234263
posts = ''
235264
error = False
236265

237-
# DISCOURSE_DEV_POST_SUFFIX is used to differentiate the same target name from different dev systems in Discourse
266+
# DISCOURSE_DEV_POST_SUFFIX is used to differentiate the same target name
267+
# from different dev systems in Discourse.
238268
# It is not intended to be used for production when there is a dedicated Discourse.
239269
if topic_title:
240270
topic_title = topic_title + settings.DISCOURSE_DEV_POST_SUFFIX
241271

242-
# Get topic_id for title
243-
try:
244-
topic = DiscourseTopic.objects.get(topic_title=topic_title)
245-
except DiscourseTopic.DoesNotExist:
246-
topic = None
247-
248-
if topic:
272+
if topic := DiscourseTopic.objects.filter(topic_title=topic_title).first():
249273
# Create Discourse client
250-
client = DiscourseClient(
251-
settings.DISCOURSE_HOST,
252-
api_username=settings.DISCOURSE_USER,
253-
api_key=settings.DISCOURSE_API_KEY,
254-
)
274+
client = _get_client()
255275
posts = topic_posts(client, topic.discourse_topic_id)
256276
else:
257277
error = True
@@ -261,18 +281,10 @@ def list_discourse_posts_for_topic(topic_title):
261281

262282

263283
def check_discourse_user(user):
264-
"""Call discourse API to check discourse user exists"""
265-
266-
# Create Discourse client for user
267-
client = DiscourseClient(
268-
settings.DISCOURSE_HOST,
269-
api_username=user.username,
270-
api_key=settings.DISCOURSE_API_KEY,
271-
)
284+
"""Call discourse API to check discourse user exists.
285+
If the user does not exit user_id will be 0.
286+
"""
272287

273-
# Check user is present in Discourse
288+
client = _get_client()
274289
error, error_message, user_id = get_user(client, user.username)
275-
if user_id == 0:
276-
return error, error_message, 0
277-
278290
return error, error_message, user_id

0 commit comments

Comments
 (0)