🎉 initial commit

This commit is contained in:
Marc Michalsky 2021-06-22 19:43:49 +02:00
commit 1d32ebfbba
No known key found for this signature in database
GPG key ID: AC2D4E00990A6767
7 changed files with 184 additions and 0 deletions

4
.example_env Normal file
View file

@ -0,0 +1,4 @@
# Copy this file and name it '.env'
REDMINE_URL="https://redmine.my-awesome-ngo.org"
REDMINE_VERSION="4.1.1"
REDMINE_API_KEY="xxxxxxxxxxx"

16
.gitignore vendored Normal file
View file

@ -0,0 +1,16 @@
# PyCharm
.idea/
# Config
config.yaml
# Environment
.env
venv/
# Templates
templates/*
!templates/example_template
# Logs
marvin.log

52
README.md Normal file
View file

@ -0,0 +1,52 @@
# Marvin
Marvin is a bot for Redmine. He helps you to tidy up your ticket system by nudging and/or closing abandoned tickets.
## Configuration
After pulling the repository you have to fulfill the requirements with:
```bash
pip3 install -r requirements
```
Next copy the example files, rename them to `.env` and `config.yaml` and fill in the right values.
### Configure actions
You can define different actions for the bot to perform.
Below the `actions` section in the `config.yaml` you can add your own actions.
```yaml
actions:
waiting_for_feedback:
start_date: "2021-01-01" # Date from which tickets are to be processed (all tickets from yyyy-mm-dd)
time_range: "14" # Number of days that should be exceeded without updates on the ticket
projects:
- "IT Tickets" # List of redmine projects
status:
- "Waiting for feedback" # List of all issue status you want to treat
close_ticket: true # Should the ticket be closed after the update?
template: "close_ticket" # Template for the update massage
in_revision:
start_date: "2021-01-01"
time_range: "14"
projects:
- "IT Tickets"
status:
- "In revision"
close_ticket: false
template: "nudge_ticket"
```
## Usage
To run the script at regular intervals, simply add it to the crontab.
Open crontab
```bash
crontab -e
```
Add entry
```
30 8 * * * python3 path/to/main.py
```
This executes the script every day at 8.30 am.

34
example_config.yaml Normal file
View file

@ -0,0 +1,34 @@
# Copy this file and name it "config.yaml"
redmine:
url: ${REDMINE_URL}
version: ${REDMINE_VERSION}
api_key: ${REDMINE_API_KEY}
issue_closed_id: 5 # Id of the "closed" status
actions:
waiting_for_feedback:
start_date: "2021-01-01" # Date from which tickets are to be processed (all tickets from yyyy-mm-dd)
time_range: "14" # Number of days that should be exceeded without updates on the ticket
projects:
- "IT Tickets" # List of redmine projects
status:
- "Waiting for feedback" # List of all issue status you want to treat
close_ticket: true # Should the ticket be closed after the update?
template: "close_ticket" # Template for the update massage
in_revision:
start_date: "2021-01-01"
time_range: "14"
projects:
- "IT Tickets"
status:
- "In revision"
close_ticket: false
template: "nudge_ticket"
logging:
level: "ERROR"

68
main.py Normal file
View file

@ -0,0 +1,68 @@
#!/usr/bin/env python3
import sys
from datetime import date, timedelta
import logging
from redminelib import Redmine
from envyaml import EnvYAML
from jinja2 import Template
def treat_issues():
# Set up logging
logging.basicConfig(
filename='marvin.log',
level=logging.ERROR,
format='%(asctime)s - %(message)s',
)
# Load configuration
try:
config = EnvYAML("config.yaml")
url = config['redmine']['url']
version = config['redmine']['version']
api_key = config['redmine']['api_key']
actions = config['actions']
logging.getLogger().setLevel(config['logging']['level'].upper())
except Exception as e:
logging.error('Could not load config.yaml', exc_info=True)
sys.exit(1)
# Create Redmine object instance
try:
redmine = Redmine(url, version=version, key=api_key)
except Exception as e:
logging.error('Could not instantiate Redmine object', exc_info=True)
sys.exit(1)
# Loop through all actions defined in config.yaml
for action in actions.values():
# Calculate end_date
end_date = date.today() - timedelta(days=+int(action['time_range']))
# Loop through affected issues
try:
for issue in redmine.issue \
.filter(updated_on=f"><{action['start_date']}|{end_date.isoformat()}") \
.filter(project__name__in=action['projects'], status__name__in=action['status'], closed_on=None):
with open(f"templates/{action['template']}", newline='\r\n') as f:
content = f.read()
template = Template(content)
notes = template.render(author=issue.author.name, time_range=action['time_range'], url=issue.url)
# Update issue
if action['close_ticket']:
redmine.issue.update(issue.id, notes=notes, status_id=config['redmine']['issue_closed_id'])
logging.info(f"Ticket ID: {issue.id}, ticket closed")
else:
redmine.issue.update(issue.id, notes=notes)
logging.info(f"Ticket ID: {issue.id}")
except Exception as e:
logging.error('Could not process issues', exc_info=True)
sys.exit(1)
if __name__ == "__main__":
treat_issues()

3
requirements Normal file
View file

@ -0,0 +1,3 @@
python-redmine~=2.3.0
envyaml~=1.8.210417
jinja2~=3.0.1

View file

@ -0,0 +1,7 @@
Hello {{ author }},
this ticket wasn't updated for at least {{ time_range }}. We therefore assume that the problem has been solved in the meantime. If the issue persists, please reopen the ticket and give us a brief update on the situation.
Here is the ticket: {{ url }}
Yours sincerely
Your IT Team