5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

進捗をGitHub風に可視化するアプリを作った話。

Posted at

具体的に作ったもの

毎日の進捗日記を投稿、閲覧するアプリ。Webアプリ処女作クオリティ:disappointed_relieved:
進捗(投降した日記の文字数)が芝の色に反映される。
(画像にある日記タイトルはテストで投稿してるのでお気になさらず)

mydiary.png

投稿する日記はmarkdown形式に対応していて閲覧するとhtml形式に変換されて表示される。
mydiary_admin.png

バージョン

python 3.6.5
Django 2.2.1

作り方

以下、プロジェクト名はmydiary、アプリの名前はblogとします。

まず、モデルを作成していきます。djangogirlsというチュートリアルを参考にさせていただきました。

日記をmarkdown形式に対応させたいので、models.pyはdjangogirlsのものに以下のように少し手を加えます。
具体的には、markdownをDjangoで扱うためのライブラリ、django-markdownxを使用します。
(こちらのサイトを参考にさせていただきました。)

blog\models.py
from django.db import models
from django.utils import timezone
from markdownx.models import MarkdownxField
from markdownx.utils import markdownify


class Post(models.Model):
    author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    # text = models.TextField()
    text = MarkdownxField('本文', help_text='Markdown形式で書いてください. ')
    created_date = models.DateTimeField(
            default=timezone.now)
    published_date = models.DateTimeField(
            blank=True, null=True)

    def publish(self):
        self.published_date = timezone.now()
        self.save()

    def text_to_markdown(self):
        return markdownify(self.text)

    def __str__(self):
        return self.title

この日記モデルを管理サイトから投稿するようにします。

blog\admin.py

from django.contrib import admin
from .models import Post

admin.site.register(Post)

urls.pyはそれぞれ以下のように指定しました。

mydiary\urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('', include('blog.urls')),
    path('admin/', admin.site.urls),
    path('markdownx/', include('markdownx.urls')),
]
blog\urls.py
from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [
    path('', views.post_list, name='post_list'),
    path('add/', views.post_edit, name='post_add'),
    path('browse/<int:post_id>/', views.post_browse, name='post_browse')
]

次に、viewを作成していきます。
簡単に、ページは以下の2つだけにします。

  • 投降した進捗日記のタイトル一覧と、github風の芝があるページ (post_list)
  • 選択した進捗日記を読むページ (post_browse)

まずtemplateからですが、base.htmlを作ります。
これは@kaki_k様のPython Django入門(4)から拝借させていただきました。

そのbase.htmlを使用して以下のようにpost_list.htmlを作成します。
これも上記のサイトを参考にさせていただいています。

blog\templates\blog\post_list.html
{% extends "blog/base.html" %}

{% block title %}日記の一覧{% endblock title %}

{% block content %}
    <h4 class="mt-4 border-bottom">日記の一覧</h4>
    <table class="table table-striped table-bordered">
      <thead>
        <tr>
            <th scope="col">日記タイトル</th>
            <th scope="col">書いた日時</th>
          <th scope="col"></th>
        </tr>
      </thead>
      <tbody>
        {% for post in posts %}
        <tr>
          <th scope="row">{{ post.title }}</th>
          <td>{{ post.published_date }}</td>
          <td align="center">
              <a href="{% url 'blog:post_browse' post_id=post.id %}" class="btn btn-outline-primary btn-sm">読む</a>
          </td>
        </tr>
        {% endfor %}
      </tbody>
    </table>
{% endblock content %}

{% block extra_css %}
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/cal-heatmap/3.3.10/cal-heatmap.css" />
{% endblock extra_css %}

{% block extra_js %}
    <script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/cal-heatmap/3.3.10/cal-heatmap.min.js"></script>
    <script type="text/javascript">
    var datas = {{ data }};
        var cal = new CalHeatMap();
        var now = new Date();
        cal.init({
            itemSelector: '#sample-heatmap',
            domain: "month",
            data: datas,
            domainLabelFormat: '%Y-%m',
            start: new Date(now.getFullYear(), now.getMonth() - 11),
            cellSize: 10,
            range: 12,
            legend: [10, 25, 50, 75, 150],
        });
    </script>

{% endblock extra_js %}

github風の芝を実装するライブラリはCal-Heatmapを使用しています。
こちらを参考にさせていただきました。

{{ }}で囲まれている部分にはviewから渡されたデータが入ります。

post_browse.htmlは以下のように書きます。シンプルすぎて味気ないですね・・・

blog\templates\blog\post_browse.html
<h1>{{ post.title }}</h1>
<p>{{ post.text_to_markdown | safe }}</p>
<p>published: {{ post.published_date }}</p>

最後にviews.pyです。

blog\templates\views.py
from django.shortcuts import render, get_object_or_404
from django.utils import timezone
from .models import Post


def post_list(request):
    posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
    unix_time = [post.published_date.timestamp() for post in posts]
    char_count = [len(post.text) for post in posts]
    data = dict(zip(unix_time, char_count))
    return render(request, 'blog/post_list.html', {
        'posts': posts,
        'data': data,
    })


def post_browse(request, post_id):
    post = get_object_or_404(Post, pk=post_id)
    return render(request, 'blog/post_browse.html', {
        'post': post,
    })

post_listでは投稿された日時をUNIX時間に直したもの(unix_time)と
進捗日記の文字数(char_count)をタプルとしてまとめたリスト(data)をtemplateに渡しています。
これがcal-heatmapのデータになっています。postspostは投降した日記のオブジェクトです。

以上です。

改良の余地は多々ありますが実現したいことはできたので「これで完成」と言わせてください。

このアプリを作った理由

  • 実験ノートを毎日markdown形式で書き溜めていて、活用したかったから。
  • 進捗を可視化してモチベーションを保ちたかったから
  • 10連休のGWに新しいことを始めたかったから。

感想

タイトルの割にしょうもない内容ですみません。
初めてのWebアプリ製作はわからないことだらけでしたがそれを一つずつ解決して
自分の思う完成に近づいていく過程がとてもたのしかったです。
たくさんのサイトにお世話になりました。感謝申し上げます。
また、ここまで読んでくださった方、ありがとうございました。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?