トップ画面処理を作る

トップに必要なもの

必要なもの一覧

トップ画面は、イベント名の一覧と、参加許可一覧を表示する画面になります。 イベント名一覧はだれでも見ることができます。ただしイベントの中に登録された写真を見ることができるのは主催者と許可された人だけです。 参加許可一覧は、自分以外の人からイベントに参加希望を受けた場合にその希望一覧を出すものです。

eventデータベース

誰がいつ作った何という名前のイベントかを保存するデータベースです。作成した日の10日後が最長期限ですので、11日間使用できます。

emailとかも入っていますが、表示していないので入れなくてもいいです。緊急連絡用です。commentで主催者にメモを送れます。

paticipがNoSQLらしいところで、ここにLISTを保存できます。リレーショナルデータベースなら、リンクを張った詳細テーブルにデータを保存するのでしょうが、リレーションができないので、この方法をとりました。参加許可された人のIDがLIST形式で保存されます。

event.py(抜粋)

import base
import photoBase
import driveFile
import datetime
import re

class Event:
    def __init__(self):
        self.deta = base.Base()
        self.event = self.deta.openBase("events")
    
    def newEvent(self, userid, eventname,  firstDay, limitdate, comment, email, paticip):
        t = datetime.date.today()
        edata = self.dirCurrentEvents(t.strftime("%Y-%m-%d"))
        if edata.count < 10:
            return False
        else:
            self.event.put({"id":userid,"eventname": eventname, "firstDay": firstDay,
            "limitdate": limitdate, "comment": comment,"email": email, "paticip": paticip})
            return True

eventのコンストラクタを呼ぶと、eventsというデータベースが作成されます。新規追加で、イベント名、登録日、期限日、コメント、email、許可リストが登録されます。

event.py(抜粋)

def dirCurrentEvents(self,thisDay:str):
    strDay = self.fmtDate(thisDay,"-")
    return self.event.fetch(query={"limitdate?gte":strDay},limit=10,last=None)
                

トップ画面に表示するのは、期限日前のイベントの一覧です。今日の日付を文字で指定してそれ以前のイベントだけ表示します。ところがこのBaseでは日付をdatetimeとして保存できるは、expire_atという、削除期限だけらしいのです。これを指定すれば自動で消えてくれるのですが、それをすると写真が残ってしまうので、今回はこれは使わず、文字列で日付を入れておいて、文字列で検索する方式にしました。

paticipデータベース

イベント主催者に対して参加希望を出したときだけできるデータベースです。

paticip.py(抜粋)

import base

class Paticip:
    def __init__(self):
        self.db = base.Base()
        self.paticip = self.db.openBase("paticip")
    
    def newRequest(self, ekey, eventname, uid, rmemo):
        self.paticip.put({"eventid":ekey,"eventname": eventname,"userid":uid, "rmemo":rmemo})
    
    def getRequest(self, dict):
        return self.paticip.fetch(query=dict)

    def deleteRequest(self,pkey):
        self.paticip.delete(pkey)

paticipのコンストラクタを呼ぶと、paticipというデータベースができます。参加希望を出すと、対象のエベントID、エベント名、参加希望者ID、希望メモが保存されます。

getRequestには検索するクエリーを辞書形式で渡します。また依頼に対する答えが出たらその依頼をdeleteRequestで削除します。

トップ画面 main.py(抜粋)

トップ画面は top()関数でデータを準備します。イベントと依頼のクラスを使ってindex.htmlに渡すべきデータを準備します。

main.py(抜粋)

@app.route("/", methods=["GET","POST"])
def top():
    """
    login済であればイベント一覧やメッセージを見ることができる
    """
    if "id" in session: 
      events = event.Event()
      t = datetime.date.today()
      edata = events.dirCurrentEvents(t.strftime("%Y-%m-%d"))
      errmess = "" if edata.count <= 10 else "イベントが満杯です"
      patic = paticip.Paticip()
      rSQL = []
      for e in edata.items:
        if e["id"] == session["id"]:
          eSQL = {}
          eSQL["eventid"] = e["key"]
          rSQL.append(eSQL)
      if rSQL:
        rdata = patic.getRequest(rSQL)
      else:
        rdata = None
      return render_template("index.html", id=session["id"],edata=edata,rdata=rdata,errmess=errmess)
    return render_template("login.html") 

今の時点で有効なイベントの一覧を取得して(edata)、ログインしている本人が主催しているeventidの辞書のリストを作り、関連する依頼のデータをデータベースから取得します。(rdata)

このデータをindex.htmlに渡して、画面表示してもらいます

トップ画面 index.html

top()関数から渡されたデータを画面表示します。

index.html

<!DOCTYPE html>
<html lang="jp">
<head>
    <meta charset="UTF-8">
    <title>メインページ</title>
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <meta name="description" content="pythonとdeta.shのサンプルページです" >
    <meta name="keywords" content="プログラム,教育,python,deta.sh,bootstrap,flask" >
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <link rel="stylesheet" type="text/css"
    href="{{ url_for('static', filename='css/style.css')}}">
</head>
<body>
  <div class="container">
    <nav class="navbar w-100 navbar-expand-sm navbar-dark bg-dark" aria-label="Third navbar example">
      <div class="container-fluid">
        <a class="navbar-brand" href="#">Event Photos</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarsExample03" aria-controls="navbarsExample03" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarsExample03">
          <ul class="navbar-nav me-auto mb-2 mb-sm-0">
            <li class="nav-item">
              <a class="nav-link active" aria-current="page" href="/">Home</a>
            </li>
            <li class="nav-item dropdown">
              <a class="nav-link dropdown-toggle" href="#" id="dropdown03" data-bs-toggle="dropdown" aria-expanded="false">操作</a>
              <ul class="dropdown-menu" aria-labelledby="dropdown03">
                <li><a class="dropdown-item" href="/event">イベント追加</a></li>
                <li><a class="dropdown-item" href="/logout">ログアウト</a></li>
              </ul>
            </li>
            <li class="nav-item">
              <a class="nav-link active" aria-current="page" href="/signup">Help</a>
            </li>
          </ul>
        </div>
      </div>
    </nav>
  <main>
  <div id="wrapper">
    <header class="d-flex justify-content-end">
      <p>ID: {{id}}  </p>
      <a href="{{ url_for('logout') }}"><button class="btn btn-danger mx-1">Logout</button></a>
    </header>
    <div class="row">
      <div class="col col-12 col-lg-6 col-sm-12 event-dir">
        <div class="d-flex flex-column align-items-stretch flex-shrink-0 bg-white mt-2">
          <div class="d-flex bg-light align-items-center flex-shrink-0 p-3 link-dark text-decoration-none border-bottom">
            <span class="fs-5 fw-semibold">イベント一覧<a href="{{ url_for('newEvent') }}"><button class="btn btn-primary mx-4">イベント追加</button></a></span>
            <span class="text-danger">{{errmess}}</span>
          </div>
          <div class="list-group list-group-flush border-bottom scrollarea">
                    {% set ns = namespace(st = "disable") %}
                    {% for item in edata.items %}
                    {% set ns.st = "text-primary" if id in item.paticip else "" %}
            <div  class="list-group-item py-3 lh-tight disable" aria-current="true" >
              <div class="d-flex w-100 align-items-center justify-content-between">
                <strong class="mb-1 {{ns.st}}" >{{item.eventname}}</strong>
                <small>期限:{{item.limitdate}}</small>
              </div>
              <div class="col-10 mb-1 small">{{item.comment}}</div>
                    {% if id in item.paticip %}
                <div class="btn-group">
                  <a class="badge" href="{{url_for('dirPhotos',ekey=item.key,photoFlag=True)}}"><button type="button" class="btn btn-sm btn-primary text-white btn-outline-secondary">写真あり一覧</button></a>
                  <a class="badge" href="{{url_for('dirPhotos',ekey=item.key,photoFlag=False)}}"><button type="button" class="btn btn-sm btn-light btn-outline-secondary">削除一覧</button></a>
                </div>
                    {% else %}
                <a class="badge" ><button type="button" class="btn btn-sm btn-light btn-outline-secondary" disabled>写真あり一覧</button></a>
                <a class="badge" ><button type="button" class="btn btn-sm btn-light btn-outline-secondary" disabled>削除一覧</button></a>
                <a class="badge" href="{{url_for('sendRequest',ekey=item.key,id=id)}}"><button type="button" class="btn btn-sm btn-light btn-outline-secondary">参加希望</button></a>
                    {% endif %}
              </div>
                    {% endfor %}
            </div>
          </div>
        </div>
        <div class="col col-12 col-lg-6 col-sm-12 message-dir">
          <div class="d-flex flex-column justify-content-between">
            <div class="mt-2">
              <div class="d-flex flex-column align-items-stretch flex-shrink-0 bg-white">
                <div class="d-flex bg-light align-items-center flex-shrink-0 p-3 link-dark text-decoration-none border-bottom">
                  <span class="fs-5 fw-semibold">許可願い一覧</span>
                </div>
                <div class="list-group list-group-flush border-bottom scrollarea">
                        {% for item in rdata.items %}
                  <div  class="list-group-item list-group-item-action py-3 lh-tight " aria-current="true" >
                    <div class="d-flex w-100 align-items-center justify-content-between">
                      <strong class="mb-1">{{item.eventname}}</strong>
                      <small>申請From:{{item.userid}}</small>
                    </div>
                    <div class="col-10 mb-1 small">{{item.rmemo}}</div>
                      <div class="btn-group">
                        <a class="badge" href="{{url_for('sendPatic',pkey=item.key,fromid=item.userid)}}"><button type="button" class="btn btn-sm btn-light btn-outline-secondary">管理</button></a>
                      </div>
                    </div>
                        {% endfor %}
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </main>  
  </div>
  <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js" integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js" integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF" crossorigin="anonymous"></script>
</body>
</html>

この画面では、自分が作成したイベント以外にも他人が作ったイベントも表示されます。そのイベントに参加希望を出して、参加が認められれば自分もそのイベントの写真が見れて、また自分も追加することができます。

流れを、画面で紹介します。hisanagaというユーザーが一つイベントを作成して写真を追加しているところにguestさんが加入してきて、そのイベントを見てみたいと思ったところから説明します。


参加希望前

guestさんが、loginしたところ一つイベントが表示されていますが、自分が参加していないので中身を見ることができません。参加希望のボタンを押して、参加希望を提出します。


参加希望記入

hisanagaさんが作ったイベントに自分も参加させてほしいと希望をだします


許可一覧

hisanagaさんは、自分のindexに許可依頼が来たことを示すリストが出てきます。管理ボタンを押してどうするか決めます


許可画面

許可するか、拒否するかを選択します


許可後

決定すると、一覧から消えます


参加可能

guestさんはイベントに参加許可されたので、写真が見える状態になりました。写真あり一覧を押せば一覧が表示されます