はじめに

backlogに自分の課題という欄があり、そこにある自分の課題をこなすのが毎日の業務となっている。

やるべきタスクは日々増える。諸事情で完了にできないけど自分が担当者になってなきゃいけない課題があったり単純に優先度低くて期日の特にない課題が残ってたりとこれでは、ぱっと見自分が今日何やんなきゃいけないのか、何が最近追加された課題なのかわからなくなってきた。

そこでTrelloみたいなボード式の課題管理しよ!って作ってみた。
言語がpythonなのは特に理由は無い。やったことない言語が好きなだけ。

前提条件

ソース

backlog_api.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import requests
import json
import cgi
import urllib.request, urllib.error

class backlog_api_class :

    json_path = "backlog_state.jsonまでのパス"
    params = cgi.FieldStorage()
    api_key = "バックログのAPIキー"
    domain = "ローカルドメイン/backlog_ajax.py"
    backlog_domain = "バックログのURL"
    my_id = 自分のユーザID
    project_ids = []
    pending = "pending"
    completion = "completion"
    sorting = "sorting"
    pending_lavel = "保留中"
    completion_lavel = "完了"
    sorting_lavel = "対応予定"
    type_array = [pending, completion, sorting]
    completion_array = []
    pending_array = []
    sorting_array = []
    resourceNotRead = []

    def api_boolean(self, json_data, type_name):
        return self.params.getvalue('is_' + type_name) is not None and self.params.getvalue('is_' + type_name) not in json_data[type_name]

    def nl2br(self, string):
        return "<br />".join(string.split("\n"))

    def load_object(self, path, params):
        return json.loads(requests.get(self.backlog_domain + "/api/v2" + path, params=params).text)

    def append_json(self, json_data, type_name):
        json_data[type_name].append(self.params.getvalue('is_' + type_name))

    def remove_json(self, json_data, type_name):
        self.type_array.remove(type_name)
        for item in self.type_array:
            if self.params.getvalue('is_' + type_name) in json_data[item]: json_data[item].remove(self.params.getvalue('is_' + type_name))

    def html_create(self, array, class_name):
        print("<div class='" + class_name + "'>")
        print("<h1 class='status'>")
        if class_name == self.sorting: print(self.sorting_lavel)
        elif class_name == self.pending: print(self.pending_lavel)
        elif class_name == self.completion: print(self.completion_lavel)
        print("</h1>")
        for item in array:
            print("<div class='item' id=" + item['issueKey'] + ">")
            if item['issueKey'] in self.resourceNotRead: print('<span class="new-icon">NEW</span>')
            print("<h2 class='summary'>" + "<a target='_blank' href='" + self.backlog_domain + "/view/" + item['issueKey'] + "'>" + item["summary"] + "</a></h2>")
            if class_name != self.pending: print("<span class='api_trigger' callback='" + self.domain + "?is_pending=" + item['issueKey'] + "'>" + self.pending_lavel + "</span>")
            if class_name != self.sorting: print("<span class='api_trigger' callback='" + self.domain + "?is_sorting=" + item['issueKey'] + "'>" + self.sorting_lavel + "</span>")
            if class_name != self.completion: print("<span class='api_trigger' callback='" + self.domain + "?is_completion=" + item['issueKey'] + "'>" + self.completion_lavel + "</span>")
            print("<p class='description'>" + self.nl2br(item["description"]) + "</p>")
            print("</div>")
        print("</div>")

    def print_date(self) :
        f_r = open(self.json_path, "r",)
        json_data = json.load(f_r)
        f_r.close()

        if self.api_boolean(json_data, self.pending):
            self.append_json(json_data, self.pending)
            if self.params.getvalue('is_pending') in json_data[self.completion] or self.params.getvalue('is_pending') in json_data[self.sorting]:
                self.remove_json(json_data, self.pending)
        elif self.api_boolean(json_data, self.completion):
            self.append_json(json_data, self.completion)
            if self.params.getvalue('is_completion') in json_data[self.pending] or self.params.getvalue('is_completion') in json_data[self.sorting]:
                self.remove_json(json_data, self.completion)
        elif self.api_boolean(json_data, self.sorting):
            self.append_json(json_data, self.sorting)
            if self.params.getvalue('is_sorting') in json_data[self.pending] or self.params.getvalue('is_sorting') in json_data[self.completion]:
                self.remove_json(json_data, self.sorting)

        f_w = open(self.json_path, "w",)
        f_w.write(str(json.dumps(json_data)))
        f_w.close()

        for item in self.load_object("/projects", {"apiKey": self.api_key}):
            self.project_ids.append(item["id"])
        for item in self.load_object("/notifications", {"apiKey": self.api_key, "count": 10}):
            if not item["resourceAlreadyRead"] and item["issue"]["assignee"]["id"] == self.my_id : self.resourceNotRead.append(item["issue"]["issueKey"])
        print("<div id='redraw'>")
        print("<div class='flex'>")
        for item in self.load_object("/issues", {"apiKey": self.api_key, "projectId[]": self.project_ids, "assigneeId[]": self.my_id, "statusId[]": [1, 2, 3]}):
            if item['issueKey'] in json_data['pending']: self.pending_array.append(item)
            elif item['issueKey'] in json_data['completion']: self.completion_array.append(item)
            else: self.sorting_array.append(item)
        self.html_create(self.sorting_array, "sorting")
        self.html_create(self.pending_array, "pending")
        self.html_create(self.completion_array, "completion")
        print("</div>")
        print("</div>")

backlog.py


#!/usr/bin/env python3
print('Content-type: text/html; charset=UTF-8\r\n')
print("""<!DOCTYPE html>
<html lang="ja" id="html" >
<head>
<link rel="stylesheet" type="text/css" href="../style.css">
<script type="text/javascript" src="../javascript.js"></script>
</head>
<body>""")

import backlog_api
backlog_api_class = backlog_api.backlog_api_class()
backlog_api_class.print_date()

print("""</body>
</html>""")

backlog_ajax.py

#!/usr/bin/env python3
print('Content-type: text/html; charset=UTF-8\r\n')
import backlog_api
backlog_api_class = backlog_api.backlog_api_class()
backlog_api_class.print_date()

backlog_state.json

{"completion": [], "pending": [], "sorting": []}

javascript.js

function XmlHttprequest(event){
	if(window.XMLHttpRequest){
		httpObj = new XMLHttpRequest();
	}else if(window.ActiveXObject){
		httpObj = new XMLHttpRequest("MSXML2.XMLHTTP.3.0");
	}
	httpObj.onreadystatechange = changeDisplay;
	httpObj.open("GET", event.target.getAttribute("callback"), true);
	httpObj.send(null);
}

function changeDisplay(){
	if(httpObj.readyState == 4 && httpObj.status == 200){
		document.getElementById("redraw").innerHTML = httpObj.responseText;
		apiTriggerSet();
	}
}
function apiTriggerSet() {
	for (var i = 0; i < document.getElementsByClassName("api_trigger").length; i++) {
		document.getElementsByClassName("api_trigger")[i].addEventListener("click", XmlHttprequest, false);
	}
}
window.onload = function() {
	apiTriggerSet();
};

style.css

html {
  overflow-x: hidden;
}
body {
	font-family: "游ゴシック体", YuGothic, "YuGothic M", sans-serif;
}
h1 {
  font-size: 30px;
}
a {
  text-decoration: none;
}
.summary {
	font-size: 15px;
}
.description {
	font-size: 14px;
  word-wrap: break-word;
}
.comments {
	font-size: 12px;
}
.flex{
    display: -webkit-flex;
    display: -moz-flex;
    display: -ms-flex;
    display: -o-flex;
    display: flex;
    justify-content: space-around;
}
.pending {
	width: 32%;
	margin: 0 0.5%;
	background: beige;
}
.completion {
	width: 32%;
	margin: 0 0.5%;
	background: aliceblue;
}
.sorting {
	width: 32%;
	margin: 0 0.5%;
	background: bisque;
}
.item {
	background: white;
	margin: 10px;
	padding: 5px;
	border-radius: 5px;
}
.api_trigger {
    display: inline-block;
    padding: 0.3em 1em;
    text-decoration: none;
    color: #67c5ff;
    border: solid 2px #67c5ff;
    border-radius: 3px;
    transition: .4s;
}

.api_trigger:hover {
    background: #67c5ff;
    color: white;
}
.status {
	text-align: center;
}
.new-icon {
  display: inline-block;
  background: #df002c;
  padding: 1px 5px;
  vertical-align: middle;
  font-size: 0.6em;
  font-weight: bold;
  color: #fff;
  border-radius: 3px;
}

簡単な説明

  1. css, js, jsonのパスを通す。
  2. backlog_api.pyにAPIキー等書く。
  3. ローカルドメイン/backlog.pyでブラウザで開けば完成!

反省点

命名がダサいし意味とズレてる、あと分岐が多いがこれも多分なんとかできるはず。
何よりありえないのが"<div>あいうえお</div>"みたいなことしちゃってるの
テンプレートエンジン使えよって話ですよね。。
次回qiitaは神社とかいじってみるかもです。。!