Skip to content

Commit

Permalink
Merge pull request #91 from stfc/wordcloud_generator
Browse files Browse the repository at this point in the history
Ticket Word Cloud Generation Script
  • Loading branch information
DavidFair authored Dec 6, 2023
2 parents 1e56165 + 8bf5917 commit 3d4eb29
Show file tree
Hide file tree
Showing 7 changed files with 667 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/black.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ jobs:
with:
src: "dns_entry_checker"

- name: Word Cloud Generator
uses: psf/black@stable
with:
src: "word_cloud_generator"

- name: JSM Metric Collection
uses: psf/black@stable
with:
Expand Down
33 changes: 33 additions & 0 deletions .github/workflows/word_cloud_generator.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Word Cloud Generator Unittest

on:
push:
branches:
- master
pull_request:
paths:
- "word_cloud_generator/**"
- ".github/workflows/word_cloud_generator.yaml"

jobs:
test_with_unit_test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
cache: "pip"

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r word_cloud_generator/requirements.txt
- name: Test with unittest
run: |
cd word_cloud_generator
python3 -m unittest test_word_cloud_generator.py
Empty file.
29 changes: 29 additions & 0 deletions word_cloud_generator/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Word Cloud Generator

## General Info

This is a Python script that when run, creates a filter word cloud from the summary of tickets over a time period.

The script takes ~10 seconds to complete on a month of tickets

Unit tests exist which test the logic of the methods the script uses, and the tests should be run whenever changes are made to the code.

## Requirements

requests: 2.31.0
parameterized: 0.9.0
python-dateutil: 2.8.2
wordcloud: 1.9.2

## Setup
Running the script:
```
$ cd ../word_cloud_generator
$ pip install -r requirements.txt
$ python3 word_cloud_generator.py
```

Running the unit tests:
```
$ python3 -m unittest discover -s ./test -p "test_*.py"
```
5 changes: 5 additions & 0 deletions word_cloud_generator/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
requests
parameterized
python-dateutil
wordcloud
mashumaro
283 changes: 283 additions & 0 deletions word_cloud_generator/test_word_cloud_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
from unittest import mock
from unittest.mock import MagicMock, patch
from parameterized import parameterized
from datetime import datetime

import requests
import requests.auth
import word_cloud_generator
import unittest


class ChangingJson:
"""
Class to represent a json object which changes value when it's called.
"""

def __init__(self, values):
"""
Constructs the attributes for the ChangingJson object
:param values: The values for the ChangingJson to change through (list)
"""
self.values = values
self.current_index = 0

def get(self, get_value):
"""
Function to emulate the Json "Get" function while cycling through the values
:param get_value: The value to requested (any)
:return: The next value currently stored in the list (any)
"""
return_value = self.values[self.current_index].get(get_value)
if get_value == "size":
self.current_index = (self.current_index + 1) % len(self.values)
return return_value


auth = requests.auth.HTTPBasicAuth("test_username", "test_password")
headers = {
"Accept": "application/json",
}
host = "https://test.com"


class WorldCloudGeneratorTests(unittest.TestCase):
"""
Class for the test to be run against the functions from word_cloud_generator.py
"""

@parameterized.expand(
[
("check found", "something-else", True),
("check not found", b'{"status":"RUNNING"}', False),
]
)
def test_get_response_json(self, __, session_response_return_value, expected_out):
"""
Function to test the functionality of get_response_json by asserting that the function
calls a specific function or raises a Timeout error
:param __: The name of the parameter, which is thrown away (string)
:param session_response_return_value: The mocked return value for the
session response (string)
:param expected_out: The expected output of the function (bool)
"""
with mock.patch("word_cloud_generator.requests") and patch(
"word_cloud_generator.json"
):
word_cloud_generator.requests.session = MagicMock()
word_cloud_generator.requests.session.return_value.get.return_value.content = (
session_response_return_value
)

word_cloud_generator.json = MagicMock()

if expected_out:
word_cloud_generator.get_response_json(auth, headers, host)

word_cloud_generator.json.loads.assert_called_once()
else:
self.assertRaises(
requests.exceptions.Timeout,
word_cloud_generator.get_response_json,
auth,
headers,
host,
)

@parameterized.expand(
[
("dates valid", "2022-01-01", ["test1", "test2", "test3", "test4"]),
("dates invalid", "2024-01-01", []),
]
)
def test_get_issues_contents_after_time(self, __, filter_date, expected_out):
"""
Function to test the functionality of get_issues_contents_after_time by asserting
that the value returned is expected
:param __: The name of the parameter, which is thrown away (string)
:param filter_date: The mocked date to filter after (list)
:param expected_out: The expected output of the function (bool)
"""
with mock.patch("word_cloud_generator.get_response_json"), mock.patch(
"word_cloud_generator.filter_issue"
):
issue_filter = word_cloud_generator.from_user_inputs(
**{
"output": None,
"start_date": None,
"end_date": filter_date,
"word_cloud": None,
}
)
values = ChangingJson(
(
{
"values": (
{
"fields": {
"summary": "test1",
"created": "2023-01-01T00:00:00",
}
},
{
"fields": {
"summary": "test2",
"created": "2023-01-01T00:00:00",
}
},
),
"size": 50,
},
{
"values": (
{
"fields": {
"summary": "test3",
"created": "2023-01-01T00:00:00",
}
},
{
"fields": {
"summary": "test4",
"created": "2023-01-01T00:00:00",
}
},
),
"size": 32,
},
)
)
word_cloud_generator.get_response_json.return_value = values
word_cloud_generator.filter_issue.return_value = True
self.assertEqual(
word_cloud_generator.get_issues_contents_after_time(
auth,
headers,
host,
issue_filter,
),
expected_out,
)

@parameterized.expand(
[
(
"dates valid",
{
"output": None,
"end_date": None,
"word_cloud": None,
"start_date": "2024-01-01",
"assigned": "test",
},
True,
),
(
"dates invalid",
{
"output": None,
"end_date": None,
"word_cloud": None,
"start_date": "2022-01-01",
"assigned": "test",
},
False,
),
(
"assigned valid",
{
"output": None,
"end_date": None,
"word_cloud": None,
"start_date": "2024-01-01",
"assigned": "test",
},
True,
),
(
"assigned invalid",
{
"output": None,
"end_date": None,
"word_cloud": None,
"start_date": "2024-01-01",
"assigned": "test failed",
},
False,
),
]
)
def test_filter_issue(self, __, issue_filter, expected_out):
"""
Function to test the functionality of filter_issue by asserting
that the value returned is expected
:param __: The name of the parameter, which is thrown away (string)
:param issue_filter: The issue filter (dict)
:param expected_out: The expected output of the function (bool)
"""
issue = {"fields": {"assignee": {"displayName": "test"}}}
issue_date = datetime.strptime("2023-01-01", "%Y-%m-%d")
issue_filter = word_cloud_generator.from_user_inputs(**issue_filter)
self.assertEqual(
word_cloud_generator.filter_issue(
issue,
issue_filter,
issue_date,
),
expected_out,
)

def test_generate_word_cloud(self):
"""
Function to test the functionality of generate_word_cloud by asserting that the function
is called with specific inputs
"""
with mock.patch("word_cloud_generator.filter_word_cloud"), mock.patch(
"word_cloud_generator.WordCloud"
):
issues_contents = "test data"
issue_filter = ""
word_cloud_output_location = "test"
word_cloud_parameters = {
"width": 2000,
"height": 1000,
"min_font_size": 25,
"max_words": 10000,
}
word_cloud_generator.generate_word_cloud(
issues_contents,
issue_filter,
word_cloud_output_location,
**word_cloud_parameters,
)
word_cloud_generator.WordCloud.return_value.generate.assert_called_with(
word_cloud_generator.filter_word_cloud.return_value
)
word_cloud_generator.WordCloud.return_value.to_file.assert_called_with(
"test"
)

def test_filter_word_cloud(self):
"""
Function to test the functionality of generate_word_cloud by asserting that the function
returns an expected value
"""
issue_filter = word_cloud_generator.from_user_inputs(
**{
"output": None,
"start_date": None,
"end_date": None,
"word_cloud": None,
"filter_not": "delete|this",
"filter_for": "data|test|this|here|delete|not",
}
)
issues_contents = "test data delete this not here"
self.assertEqual(
word_cloud_generator.filter_word_cloud(issue_filter, issues_contents),
"test data not here",
)


if __name__ == "__main__":
unittest.main()
Loading

0 comments on commit 3d4eb29

Please sign in to comment.