Refactored Github asset management code, changed to use domains and color-coded labels. See README for updated info.
This commit is contained in:
parent
090ccc3011
commit
a84db65d93
36
README.md
36
README.md
|
@ -451,30 +451,42 @@ SVG files are printed as text currently, avoid printing them.
|
||||||
|
|
||||||
This module is disabled by default.
|
This module is disabled by default.
|
||||||
|
|
||||||
### Github based hackerspace asset management
|
### Github based asset management
|
||||||
|
|
||||||
This module was written for asset (machines, tasks and todo) management with
|
This module was written for asset (machines, tasks and todo) management by
|
||||||
GitHub issues and labels.
|
mis-using GitHub issues and labels. It has been designed to be used with hackerspace
|
||||||
|
environment but can be extended to any purpose.
|
||||||
|
|
||||||
Create labels to github that represent machines (M: ) and spaces (S: ).
|
#### Github project setup
|
||||||
When creating issues, assign machine and/or space labels for them.
|
|
||||||
|
* Create labels to github that represent for example different machines and spaces.
|
||||||
|
You can create any number of them.
|
||||||
|
* Define label colors for each type of asset. These are called domains in this module.
|
||||||
|
For example set all machine labels to be #B60205 and space labels to be #0E8A16. These
|
||||||
|
can be easily picked from color chooser.
|
||||||
|
* Edit the repository description and add a json block describing the
|
||||||
|
label domains and their colors. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
Hackerspace machines, todo and stuff. domains={ "machines": "#B60205", "spaces" : "#0E8A16"}
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure you type the description on one line - this is a silly Github limitation.
|
||||||
|
|
||||||
|
* When creating issues, assign machine and/or space labels for them.
|
||||||
For example, if a wood lathe is broken, create issue with labels
|
For example, if a wood lathe is broken, create issue with labels
|
||||||
"M: Wood lathe" and "S: Wood working room".
|
"Wood lathe" and "Wood working room".
|
||||||
|
|
||||||
Usage:
|
#### Usage
|
||||||
|
|
||||||
* !ghproj setrepo [repository] - Set repository for this room (room admin only)
|
* !ghproj setrepo [repository] - Set repository for this room (room admin only)
|
||||||
* !ghproj repo - Shows which repository this room tracks
|
* !ghproj repo - Shows which repository this room tracks
|
||||||
* !ghproj rmrepo - Remove repository from this room (room admin only)
|
* !ghproj rmrepo - Remove repository from this room (room admin only)
|
||||||
* !ghproj machines - List machine statuses for this room
|
* !ghproj [domain] - List machine statuses in this domain
|
||||||
* !ghproj spaces - List statuses of spaces
|
|
||||||
|
|
||||||
Repository name must be in format TampereHacklab/Inventaario - you can
|
Repository name must be in format TampereHacklab/Inventaario - you can
|
||||||
use this as a example to see how the labels work.
|
use this as a example to see how the labels work.
|
||||||
|
|
||||||
In future this might be used as general purpose project management
|
|
||||||
module. PR's welcome!
|
|
||||||
|
|
||||||
## Bot setup
|
## Bot setup
|
||||||
|
|
||||||
* Create a Matrix user
|
* Create a Matrix user
|
||||||
|
|
|
@ -1,14 +1,75 @@
|
||||||
from github import Github
|
from github import Github
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
|
||||||
from modules.common.module import BotModule
|
from modules.common.module import BotModule
|
||||||
|
|
||||||
|
# Helper class with reusable code for github project stuff
|
||||||
|
class GithubProject:
|
||||||
|
def get_domains(description):
|
||||||
|
p = re.compile('domains=\{.*\}')
|
||||||
|
matches = json.loads(p.findall(description)[0][8:])
|
||||||
|
return matches
|
||||||
|
|
||||||
|
def get_domain(reponame, domain):
|
||||||
|
g = Github()
|
||||||
|
repo = g.get_repo(reponame)
|
||||||
|
domains = GithubProject.get_domains(repo.description)
|
||||||
|
if(not len(domains)):
|
||||||
|
return None, None
|
||||||
|
domain_color = domains.get(domain, None)
|
||||||
|
if not domain_color:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
open_issues = repo.get_issues(state='open')
|
||||||
|
domain_labels = []
|
||||||
|
labels = repo.get_labels()
|
||||||
|
for label in labels:
|
||||||
|
if label.color == domain_color[1:]:
|
||||||
|
domain_labels.append(label)
|
||||||
|
|
||||||
|
domain_issues = dict()
|
||||||
|
domain_ok = []
|
||||||
|
for label in domain_labels:
|
||||||
|
label_issues = []
|
||||||
|
for issue in open_issues:
|
||||||
|
if label in issue.labels:
|
||||||
|
label_issues.append(issue)
|
||||||
|
if len(label_issues):
|
||||||
|
domain_issues[label.name] = label_issues
|
||||||
|
else:
|
||||||
|
domain_ok.append(label.name)
|
||||||
|
|
||||||
|
return domain_issues, domain_ok
|
||||||
|
|
||||||
|
def domain_to_string(reponame, issues, ok):
|
||||||
|
text_out = reponame + ":\n"
|
||||||
|
for label in issues.keys():
|
||||||
|
text_out = text_out + f'{label}: '
|
||||||
|
for issue in issues[label]:
|
||||||
|
# todo: add {issue.html_url} when URL previews can be disabled
|
||||||
|
text_out = text_out + f'[{issue.title}] '
|
||||||
|
text_out = text_out + f'\n'
|
||||||
|
|
||||||
|
text_out = text_out + " OK : " + ', '.join(ok)
|
||||||
|
return text_out
|
||||||
|
|
||||||
|
def domain_to_html(reponame, issues, ok):
|
||||||
|
html_out = f'<b>{reponame}:</b> <br/>'
|
||||||
|
for label in issues.keys():
|
||||||
|
html_out = html_out + f'🚧 {label}: '
|
||||||
|
for issue in issues[label]:
|
||||||
|
# todo: add {issue.html_url} when URL previews can be disabled
|
||||||
|
html_out = html_out + f'[{issue.title}] '
|
||||||
|
html_out = html_out + f'<br/>'
|
||||||
|
|
||||||
|
html_out = html_out + " OK ☑️ " + ', '.join(ok)
|
||||||
|
return html_out
|
||||||
|
|
||||||
class MatrixModule(BotModule):
|
class MatrixModule(BotModule):
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
super().__init__(name)
|
super().__init__(name)
|
||||||
self.repo_rooms = dict()
|
self.repo_rooms = dict()
|
||||||
self.machine_prefix = "M: "
|
|
||||||
self.space_prefix = "S: "
|
|
||||||
|
|
||||||
async def matrix_message(self, bot, room, event):
|
async def matrix_message(self, bot, room, event):
|
||||||
args = event.body.split()
|
args = event.body.split()
|
||||||
|
@ -24,17 +85,15 @@ class MatrixModule(BotModule):
|
||||||
if args[0] == 'repo':
|
if args[0] == 'repo':
|
||||||
await bot.send_text(room, f'Github repo for this room is {self.repo_rooms.get(room.room_id, "not set")}.')
|
await bot.send_text(room, f'Github repo for this room is {self.repo_rooms.get(room.room_id, "not set")}.')
|
||||||
return
|
return
|
||||||
if args[0] == 'machines':
|
|
||||||
|
domain = args[0]
|
||||||
reponame = self.repo_rooms.get(room.room_id, None)
|
reponame = self.repo_rooms.get(room.room_id, None)
|
||||||
if reponame:
|
if reponame:
|
||||||
await self.get_machine_status(bot, room, reponame, self.machine_prefix)
|
issues, ok = GithubProject.get_domain(reponame, domain)
|
||||||
|
if issues or ok:
|
||||||
|
await self.send_domain_status(bot, room, reponame, issues, ok)
|
||||||
else:
|
else:
|
||||||
await bot.send_text(room, f'No github repo set for this room. Use setrepo to set it.')
|
await bot.send_text(room, f'No labels with domain {domain} found.')
|
||||||
return
|
|
||||||
if args[0] == 'spaces':
|
|
||||||
reponame = self.repo_rooms.get(room.room_id, None)
|
|
||||||
if reponame:
|
|
||||||
await self.get_machine_status(bot, room, reponame, self.space_prefix)
|
|
||||||
else:
|
else:
|
||||||
await bot.send_text(room, f'No github repo set for this room. Use setrepo to set it.')
|
await bot.send_text(room, f'No github repo set for this room. Use setrepo to set it.')
|
||||||
return
|
return
|
||||||
|
@ -53,42 +112,14 @@ class MatrixModule(BotModule):
|
||||||
|
|
||||||
await bot.send_text(room, 'Unknown command')
|
await bot.send_text(room, 'Unknown command')
|
||||||
|
|
||||||
async def get_machine_status(self, bot, room, reponame, prefix):
|
async def send_domain_status(self, bot, room, reponame, issues, ok):
|
||||||
g = Github()
|
text_out = GithubProject.domain_to_string(reponame, issues, ok)
|
||||||
repo = g.get_repo(reponame)
|
html_out = GithubProject.domain_to_html(reponame, issues, ok)
|
||||||
open_issues = repo.get_issues(state='open')
|
|
||||||
labels = repo.get_labels()
|
|
||||||
machine_status = dict()
|
|
||||||
working_machines = []
|
|
||||||
for label in labels:
|
|
||||||
if prefix in label.name:
|
|
||||||
machine_name = label.name[len(prefix):]
|
|
||||||
machine_status[machine_name] = []
|
|
||||||
for issue in open_issues:
|
|
||||||
if label in issue.labels:
|
|
||||||
machine_status[machine_name].append(issue)
|
|
||||||
if not machine_status[machine_name]:
|
|
||||||
working_machines.append(machine_name)
|
|
||||||
del machine_status[machine_name]
|
|
||||||
|
|
||||||
text_out = reponame + ":\n"
|
|
||||||
html_out = f'<b>{reponame}:</b> <br/>'
|
|
||||||
for machine in machine_status.keys():
|
|
||||||
text_out = text_out + f'{machine}: '
|
|
||||||
html_out = html_out + f'🚧 {machine}: '
|
|
||||||
for issue in machine_status[machine]:
|
|
||||||
text_out = text_out + f'{issue.title} ({issue.html_url}) '
|
|
||||||
html_out = html_out + f'<a href="{issue.html_url}">{issue.title}</a> '
|
|
||||||
text_out = text_out + f'\n'
|
|
||||||
html_out = html_out + f'<br/>'
|
|
||||||
|
|
||||||
text_out = text_out + " OK : " + ', '.join(working_machines)
|
|
||||||
html_out = html_out + " OK ☑️ " + ', '.join(working_machines)
|
|
||||||
await bot.send_html(room, html_out, text_out)
|
await bot.send_html(room, html_out, text_out)
|
||||||
|
|
||||||
|
|
||||||
def help(self):
|
def help(self):
|
||||||
return 'Github hacklab asset management'
|
return 'Github asset management'
|
||||||
|
|
||||||
def get_settings(self):
|
def get_settings(self):
|
||||||
data = super().get_settings()
|
data = super().get_settings()
|
||||||
|
|
Loading…
Reference in New Issue