必要なもの一覧
トップ画面は、イベント名の一覧と、参加許可一覧を表示する画面になります。 イベント名一覧はだれでも見ることができます。ただしイベントの中に登録された写真を見ることができるのは主催者と許可された人だけです。 参加許可一覧は、自分以外の人からイベントに参加希望を受けた場合にその希望一覧を出すものです。
eventデータベース
誰がいつ作った何という名前のイベントかを保存するデータベースです。作成した日の10日後が最長期限ですので、11日間使用できます。ただ、期限前であればその日から1週間延長できるようにしました。
emailとかも入っていますが、表示していないので入れなくてもいいです。緊急連絡用です。commentで主催者にメモを送れます。
deta.spaceのNoSQLに似せるためにLISTを保存できます。リレーショナルデータベースなら、リンクを張った詳細テーブルにデータを保存するのでしょうが、この方法をとりました。参加許可された人のIDがLIST形式で保存されます。
event.py(抜粋)
from photoBase import PhotoBase import datetime import re class Event: def __init__(self,connect,cursor): self.connect = connect self.cursor = cursor def newEvent(self, userid, eventname, firstDay, limitdate, comment, email, paticip): t = datetime.date.today() edata = self.dirCurrentEvents(t.strftime("%Y-%m-%d")) if len(edata) > 10: return False else: sql = 'insert into events (ownername,eventname,firstDay,limitdate,comment,email,paticip) values ("{0}","{1}","{2}","{3}","{4}","{5}","{6}");'.format( userid,eventname,firstDay,limitdate,comment,email,userid) self.cursor.execute(sql) self.connect.commit() return True
eventのコンストラクタを呼ぶと、login時につないだデータベースの接続を利用できます。新規追加で、イベント名、登録日、期限日、コメント、email、許可リストが登録されます。
event.py(抜粋)
def dirCurrentEvents(self,thisDay:str): strDay = self.fmtDate(thisDay,"-") result = [] sql = 'select * from events where limitdate>="{0}"'.format(strDay) self.cursor.execute(sql) rows = self.cursor.fetchall() for row in rows: row['paticip'] = self.str2list(row['paticip']) result.append(row) return result
トップ画面に表示するのは、期限日前のイベントの一覧です。今日の日付を指定してそれ以前のイベントだけ表示します。
paticipデータベース
イベント主催者に対して参加希望を出したときだけできるデータベースです。
paticip.py(抜粋)
from event import Event class Paticip: def __init__(self,connect,cursor): self.connect = connect self.cursor = cursor def newRequest(self, ekey, eventname, uid, rmemo): sql = 'insert into paticip (eventID,eventname,userid,rmemo) values ({0},"{1}","{2}","{3}")'.format( ekey,eventname,uid,rmemo) self.cursor.execute(sql) self.connect.commit() return True def havePatic(self, fromid, ekey): """ 該当エベントのpaticipの中に申請者の名前があるか Parameters: fromid: 申請者の名前 ekey: エベントの番号 Returns: True: 申請済み False: 未申請 """ sql = 'select * from events where ekey = {0}'.format(ekey) self.cursor.execute(sql) rows = self.cursor.fetchall() events = Event(self.connect,self.cursor) paticip = events.str2list(rows[0]['paticip']) return fromid in paticip def getRequest(self, myElist:list): """ paticptテーブルにある自分宛てのリクエストを探す Parameters: myElist: 自分が作ったイベントの辞書の[{'eventid': 2}]のリスト Returns: 自分宛てのpaticipレコードのリスト """ result = [] for my in myElist: sql = 'select * from paticip where eventID = {0}'.format(my['eventid']) self.cursor.execute(sql) rows = self.cursor.fetchall() for row in rows: result.append(row) return result
paticipのコンストラクタを呼ぶと、login時につないだデータベースの接続を利用できます。参加希望を出すと、対象のイベントID、イベント名、参加希望者ID、希望メモが保存されます。
getRequestには検索するクエリーを辞書形式で渡します。また依頼に対する答えが出たらその依頼をdeleteRequestで削除します。
トップ画面 app.py(抜粋)
トップ画面は top()関数でデータを準備します。イベントと依頼のクラスを使ってindex.htmlに渡すべきデータを準備します。
app.py(抜粋)
@app.route("/", methods=["GET","POST"]) def top(): """ login済であればイベント一覧やメッセージを見ることができる """ if "id" in session: events = Event(con,cur) t = datetime.date.today() edata = events.dirCurrentEvents(t.strftime("%Y-%m-%d")) errmess = "" if len(edata) <= 10 else "イベントが満杯です" patic = Paticip(con,cur) rSQL = [] for e in edata: if e["ownername"] == session["id"]: eSQL = {} eSQL["eventid"] = int(e["ekey"]) rSQL.append(eSQL) if rSQL: rdata = patic.getRequest(rSQL) else: rdata = [] 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とpythonanywhereのサンプルページです" > <meta name="keywords" content="プログラム,教育,python,pythonanywhere,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 %} {% 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"> <a href="{{ url_for('editEvent',ekey=item.ekey) }}"><strong class="mb-1 {{ns.st}}" >{{item.eventname}}</strong></a> <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.ekey)}}"><button type="button" class="btn btn-sm btn-primary text-white 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" href="{{url_for('sendRequest',ekey=item.ekey,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 %} <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['ckey'],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さんはイベントに参加許可されたので、写真が見える状態になりました。写真あり一覧を押せば一覧が表示されます