Skip to content
This repository was archived by the owner on Oct 5, 2025. It is now read-only.

Commit 3b32ae1

Browse files
authored
Added support for logging (#19)
* Added logging feature * Reformatted code slightly * Added logging to various utility methods * Added debugs * Added a series of sources to existing queries * Added a few more resources * Added a few more resources
1 parent 5617dc7 commit 3b32ae1

File tree

3 files changed

+69
-9
lines changed

3 files changed

+69
-9
lines changed

pymon.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import logging
12
import os
3+
import pathlib
4+
from logging.handlers import RotatingFileHandler
25

36
import discord
47
from discord import Message
@@ -8,9 +11,27 @@
811

912
import pymon_utils as utils
1013

11-
__version__ = "0.5.0"
14+
__version__ = "0.6.0"
1215

1316

17+
# Setup logging
18+
logs_path = pathlib.Path(
19+
os.path.abspath(os.path.dirname(__file__)),
20+
"logs"
21+
)
22+
logs_path.mkdir(parents=True, exist_ok=True)
23+
logging.basicConfig(
24+
handlers=[RotatingFileHandler(
25+
logs_path / "bot.log",
26+
backupCount=10,
27+
maxBytes=1000000
28+
)],
29+
level=logging.DEBUG,
30+
format='%(asctime)s,%(msecs)d %(levelname)-8s [%(filename)s:%(lineno)d] %(message)s',
31+
datefmt='%Y-%m-%d:%H:%M:%S',
32+
)
33+
log = logging.getLogger(__name__)
34+
1435
# Global variables
1536
client = commands.Bot(
1637
command_prefix=commands.when_mentioned_or("!"),
@@ -168,4 +189,5 @@ async def _refresh(ctx):
168189
await ctx.send(f"{len(diff)} queries modified and/or added.")
169190

170191

171-
client.run(os.environ.get("DISCORD_TOKEN"))
192+
if __name__ == '__main__':
193+
client.run(os.environ.get("DISCORD_TOKEN"))

pymon_utils.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import json
2+
import logging
23
import os
34
import string
45
from urllib.request import urlopen
56

67
from dotenv import load_dotenv
78

9+
log = logging.getLogger(__name__)
10+
811

912
def generate_keyword_mapping(queries: list) -> dict:
1013
"""
@@ -13,6 +16,7 @@ def generate_keyword_mapping(queries: list) -> dict:
1316
:param queries: a list of queries with responses
1417
:return: a dictionary of keywords to query indices
1518
"""
19+
log.debug(f"Generating keyword mapping from following list: {queries}")
1620
keyword_to_queries = dict()
1721
for i, question in enumerate(queries):
1822
if question.get('query'):
@@ -26,6 +30,7 @@ def generate_keyword_mapping(queries: list) -> dict:
2630
keyword_to_queries.setdefault(keyword, {})
2731
keyword_to_queries[keyword].setdefault(i, 0)
2832
keyword_to_queries[keyword][i] += 1
33+
log.debug(f"Generated keyword mapping as follows: {keyword_to_queries}")
2934
return keyword_to_queries
3035

3136

@@ -36,14 +41,16 @@ def generate_keywords(query: string) -> list:
3641
:param query: a search query
3742
:return: the list of keywords from that query
3843
"""
44+
log.debug(f"Generating keywords from following query: {query}")
3945
stop_words = ["", "is", "a", "the", "can",
4046
"i", "to", "in", "by", "from", "be", "of",
41-
"what", "where", "when", "why", "how", "which"]
47+
"what", "where", "when", "why", "how", "which", "and"]
4248
keywords = query \
4349
.translate(str.maketrans('', '', string.punctuation)) \
4450
.lower() \
4551
.split(" ")
4652
keywords = [word for word in keywords if word not in stop_words]
53+
log.debug(f"Generated list of keywords as follows: {keywords}")
4754
return keywords
4855

4956

@@ -55,14 +62,18 @@ def search(keyword_to_queries: dict, keywords: list) -> list:
5562
:param keywords: a list of keywords to lookup
5663
:return: a list of query indices
5764
"""
65+
log.debug(f"Searching for matching queries from the following list of keywords: {keywords}")
5866
query_count = dict()
5967
for keyword in keywords:
6068
query_indices = keyword_to_queries.get(keyword, {})
6169
for i, weight in query_indices.items():
6270
query_count.setdefault(i, 0)
6371
query_count[i] += weight
64-
best_matches = list(
65-
dict(sorted(query_count.items(), key=lambda item: item[1], reverse=True)).keys())
72+
log.debug(f"Generated dictionary of query counts: {query_count}")
73+
best_matches = list(dict(
74+
sorted(query_count.items(), key=lambda item: item[1], reverse=True)
75+
).keys())
76+
log.debug(f"Found closest matches as follows: {best_matches}")
6677
return best_matches
6778

6879

@@ -73,12 +84,14 @@ def generate_similar_queries(queries: list, keyword_to_queries: dict) -> None:
7384
:param queries: a list of queries
7485
:param keyword_to_queries: a mapping of keywords to query indices
7586
"""
87+
log.debug("Adding similar_queries field to queries list")
7688
for i, query in enumerate(queries):
7789
if i > 0:
7890
keywords = generate_keywords(query["query"])
7991
top_ids = search(keyword_to_queries, keywords)
8092
top_ids.remove(i)
8193
query["similar_queries"] = top_ids
94+
log.debug(f"Updated query to include similar queries: {query}")
8295

8396

8497
def create_md_link(url: string, text: string) -> string:
@@ -89,6 +102,7 @@ def create_md_link(url: string, text: string) -> string:
89102
:param text: the text to display
90103
:return: the markdown link
91104
"""
105+
log.debug(f"Creating markdown link from url ({url}) and text ({text}).")
92106
if url:
93107
return f"[{text}]({url})"
94108
return text
@@ -104,6 +118,7 @@ def load_knowledge() -> tuple[int, list]:
104118
:return: a tuple of the type of knowledge database and the
105119
knowledge database (0 for remote, 1 for local, 2 for default)
106120
"""
121+
log.debug("Loading Pymon's brain from knowledge path.")
107122
if path := os.environ.get("KNOWLEDGE_PATH"):
108123
try:
109124
data = urlopen(path).read().decode("utf-8")
@@ -123,6 +138,7 @@ def refresh_knowledge() -> tuple[list, dict]:
123138
:return: a tuple of the knowledge database and a mapping of
124139
keywords to query indices
125140
"""
141+
log.debug("Refreshes Pymon's brain assuming new data exists.")
126142
load_dotenv()
127143
_, queries = load_knowledge()
128144
keyword_mapping = generate_keyword_mapping(queries)
@@ -138,10 +154,12 @@ def generate_tags_set(queries: list) -> set:
138154
:param queries: the list of queries from Pymon's brain
139155
:return: a set of tags represented in Pymon's brain
140156
"""
157+
log.debug(f"Generating the unique set of tags from following list of queries: {queries}")
141158
tags = set()
142159
for query in queries:
143160
query_tags = query.get("tags", [])
144161
tags |= set(query_tags)
162+
log.debug(f"Generated list of unique tags as follows: {tags}")
145163
return tags
146164

147165

@@ -154,8 +172,10 @@ def get_queries_from_tag(queries: list, tag: str) -> list[tuple[int, dict]]:
154172
:param tag: a tag to lookup
155173
:return: a list of tuples in the form (query ID, query)
156174
"""
175+
log.debug(f"Getting set of queries that include the following tag: {tag}")
157176
matches = list()
158177
for i, query in enumerate(queries):
159178
if tag in query.get("tags", []):
160179
matches.append((i, query))
180+
log.debug(f"Found queries that match tag as follows: {matches}")
161181
return matches

queries.json

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
{
2828
"query": "What is testing?",
2929
"response": "Testing is the process of evaluating a program for bugs. It cannot be used to determine correctness of a program. It is used in conjuction with debugging to resolve bugs.",
30+
"resource": "https://therenegadecoder.com/code/junit-testing-in-java/",
3031
"credit": ["Jeremy Grifski"],
3132
"tags": ["Exam 2"]
3233
},
@@ -52,6 +53,7 @@
5253
{
5354
"query": "What is unit testing?",
5455
"response": "Unit testing is a specific form of program testing where the focus is on a small unit of the program, usually a single method. A common unit testing framework in Java is JUnit.",
56+
"resource": "https://therenegadecoder.com/code/junit-testing-in-java/",
5557
"credit": ["Jeremy Grifski"],
5658
"tags": ["Exam 2"]
5759
},
@@ -63,6 +65,7 @@
6365
{
6466
"query": "What is confidence building?",
6567
"response": "Confidence building is an approach to reasoning about a recursive algorithm. Confidence building begins by testing the recursive algorithm with the smallest problem. It then tests the recursive algorithm with the next smallest problem, and so on. By subbing in correct answers to smaller problems, we can build confidence in the recursive algorithm.",
68+
"resource": "https://therenegadecoder.com/code/5-tips-for-making-sense-of-recursion/",
6669
"credit": ["Jeremy Grifski"],
6770
"tags": ["Exam 2"]
6871
},
@@ -72,6 +75,12 @@
7275
"credit": ["Jeremy Grifski"],
7376
"tags": ["Exam 2"]
7477
},
78+
{
79+
"query": "What is the static type of a variable?",
80+
"response": "Sometimes called the declared type or the compile-time type, the static type is the type of a variable that the compiler uses to perform type checking. The static type is often called the declared type because it is the type of the variable when it is declared.",
81+
"credit": ["Jeremy Grifski"],
82+
"tags": ["Exam 2"]
83+
},
7584
{
7685
"query": "What is method overriding?",
7786
"response": "Method overriding occurs when a method from a parent class is reimplemented in the child class. An overridden method can appear in multiple classes, each with the same name and parameters.",
@@ -81,6 +90,7 @@
8190
{
8291
"query": "What is method overloading?",
8392
"response": "Method overloading occurs when multiple methods with the same name have different parameters.",
93+
"resource": "https://therenegadecoder.com/code/explain-like-im-five-method-overloading/",
8494
"credit": ["Jeremy Grifski"],
8595
"tags": ["Exam 2"]
8696
},
@@ -111,12 +121,14 @@
111121
{
112122
"query": "What is a parameter mode?",
113123
"response": "A parameter mode is a way to specify what will happen to a parameter when the method is called. In our course, there are four main parameter modes: restores, clears, updates, and replaces.",
124+
"resource": "https://therenegadecoder.com/code/improve-code-readability-by-using-parameter-modes/",
114125
"credit": ["Jeremy Grifski"],
115126
"tags": ["Exam 2"]
116127
},
117128
{
118129
"query": "What is restores mode?",
119130
"response": "Restores mode is the default parameter mode. If a parameter is in restores mode, the method promises that the parameter will not be modified from the perspective of the client. It is okay for a method to modify the parameter, but they must always restore it before the method returns.",
131+
"resource": "https://therenegadecoder.com/code/improve-code-readability-by-using-parameter-modes/",
120132
"credit": ["Jeremy Grifski"],
121133
"tags": ["Exam 2"]
122134
},
@@ -167,12 +179,14 @@
167179
{
168180
"query": "What is replaces mode?",
169181
"response": "Replaces mode is a parameter mode that indicates that a parameter will change regardless of its initial value. In this course, we indicate replaces mode using the JavaDoc annotation @replaces.",
182+
"resource": "https://therenegadecoder.com/code/improve-code-readability-by-using-parameter-modes/",
170183
"credit": ["Shaan Patel"],
171184
"tags": ["Exam 2"]
172185
},
173186
{
174187
"query": "What is updates mode?",
175188
"response": "Updates mode is a parameter mode which describes a parameter that is to be changed to some other value. Under this mode, the initial value of the parameter matters. In this course, we indicate updates mode using the JavaDoc annotation @updates.",
189+
"resource": "https://therenegadecoder.com/code/improve-code-readability-by-using-parameter-modes/",
176190
"credit": ["Colin Russel"],
177191
"tags": ["Exam 2"]
178192
},
@@ -191,12 +205,14 @@
191205
{
192206
"query": "What is clears mode?",
193207
"response": "Clears mode is a parameter mode where the method resets the value of the parameter to a predetermined initial value. Clears mode is indicated through the @clears JavaDoc annotation.",
208+
"resource": "https://therenegadecoder.com/code/improve-code-readability-by-using-parameter-modes/",
194209
"credit": ["Ethan Chilton"],
195210
"tags": ["Exam 2"]
196211
},
197212
{
198213
"query": "What is the default parameter mode when none is specified in the method contract?",
199214
"response": "When no parameter mode is specified in the method contract, the default parameter mode is Restores. This means that the variable should be restored to its original value/state after the method is called.",
215+
"resource": "https://therenegadecoder.com/code/improve-code-readability-by-using-parameter-modes/",
200216
"credit": ["Dan Brace"],
201217
"tags": ["Exam 2"]
202218
},
@@ -255,7 +271,7 @@
255271
"tags": ["Exam 2"]
256272
},
257273
{
258-
"query": "What is object-oriented-programming?",
274+
"query": "What is object-oriented programming (OOP)?",
259275
"response": "Object-oriented programming is programming with objects that contain both data and methods, which we can re-use. It allows us to implement and conceptualize real-world entities such as cars, animals, and beverages. We can use techniques such as polymorphism to use the same method to perform different tasks, hence the idea of 'many form'.",
260276
"credit": ["Felix Ji"],
261277
"tags": ["Exam 2"]
@@ -365,18 +381,19 @@
365381
{
366382
"query": "What are the four parameter modes?",
367383
"response": "The four parameter modes are: Restores, Clears, Replaces, and Updates.",
384+
"resource": "https://therenegadecoder.com/code/improve-code-readability-by-using-parameter-modes/",
368385
"credit": ["Darrel Jobin"],
369386
"tags": ["Exam 2"]
370387
},
371388
{
372-
"query": "What are NaturalNumberKernal's three methods?",
373-
"response": "NaturalNumberKernal's three methods are multiplyBy10, divideBy10, and isZero.",
389+
"query": "What are NaturalNumberKernel's three methods?",
390+
"response": "NaturalNumberKernel's three methods are multiplyBy10, divideBy10, and isZero.",
374391
"credit": ["Alyssa Wiegman", "Catherine Wu"],
375392
"tags": ["Exam 2"]
376393
},
377394
{
378395
"query": "What is a procedure?",
379-
"response": "A procedure (statement) is a method that has side effects and does not have a return value typically (void return type).",
396+
"response": "A procedure is a method that has side effects and does not have a return value typically (void return type).",
380397
"credit": ["Amit Bharathan"],
381398
"tags": ["Exam 1"]
382399
},
@@ -401,6 +418,7 @@
401418
{
402419
"query": "Is restores mode a default parameter mode?",
403420
"response": "Yes, restores mode is the default parameter mode.",
421+
"resource": "https://therenegadecoder.com/code/improve-code-readability-by-using-parameter-modes/",
404422
"credit": ["Allen Zhang"],
405423
"tags": ["Exam 2"]
406424
},

0 commit comments

Comments
 (0)