stc

a simple time card webapp
git clone _git@git.brennen.work:stc.git
Log | Files | Refs | README

commit 16fb5b74838a12e590a214f057da5eba41e3c691
parent 9501aa39fee16fe57a704d691e12c31e43ff1b35
Author: Youth Employment Program Production <youthemployment22@gmail.com>
Date:   Tue,  9 Apr 2024 10:30:36 -0600

create meetings blueprint

Diffstat:
Mapp/__init__.py | 3+++
Aapp/meetings/__init__.py | 5+++++
Aapp/meetings/__pycache__/__init__.cpython-310.pyc | 0
Aapp/meetings/__pycache__/forms.cpython-310.pyc | 0
Aapp/meetings/__pycache__/meeting.cpython-310.pyc | 0
Aapp/meetings/__pycache__/routes.cpython-310.pyc | 0
Aapp/meetings/__pycache__/update.cpython-310.pyc | 0
Aapp/meetings/forms.py | 24++++++++++++++++++++++++
Aapp/meetings/meeting.py | 0
Aapp/meetings/routes.py | 225+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/meetings/templates/dev.html | 13+++++++++++++
Aapp/meetings/templates/error.html | 12++++++++++++
Aapp/meetings/templates/form.html | 22++++++++++++++++++++++
Aapp/meetings/templates/meeting.html | 23+++++++++++++++++++++++
Aapp/meetings/templates/meetings.html | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Aapp/meetings/templates/new.html | 22++++++++++++++++++++++
Aapp/meetings/templates/update.html | 32++++++++++++++++++++++++++++++++
Aapp/meetings/templates/upload.html | 29+++++++++++++++++++++++++++++
Aapp/meetings/update.py | 0
19 files changed, 459 insertions(+), 0 deletions(-)

diff --git a/app/__init__.py b/app/__init__.py @@ -4,4 +4,7 @@ from config import Config app = Flask(__name__) app.config.from_object(Config) +from app.meetings import bp as meetings_bp +app.register_blueprint(meetings_bp) #app.register_blueprint(meetings_bp, url_prefix='/meeting') + from app import routes, models diff --git a/app/meetings/__init__.py b/app/meetings/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + +bp = Blueprint('meetings',__name__, template_folder='templates') + +from app.meetings import routes diff --git a/app/meetings/__pycache__/__init__.cpython-310.pyc b/app/meetings/__pycache__/__init__.cpython-310.pyc Binary files differ. diff --git a/app/meetings/__pycache__/forms.cpython-310.pyc b/app/meetings/__pycache__/forms.cpython-310.pyc Binary files differ. diff --git a/app/meetings/__pycache__/meeting.cpython-310.pyc b/app/meetings/__pycache__/meeting.cpython-310.pyc Binary files differ. diff --git a/app/meetings/__pycache__/routes.cpython-310.pyc b/app/meetings/__pycache__/routes.cpython-310.pyc Binary files differ. diff --git a/app/meetings/__pycache__/update.cpython-310.pyc b/app/meetings/__pycache__/update.cpython-310.pyc Binary files differ. diff --git a/app/meetings/forms.py b/app/meetings/forms.py @@ -0,0 +1,24 @@ +from flask_wtf import FlaskForm +from wtforms import StringField, SubmitField, PasswordField, BooleanField, SelectField, TimeField, DateField, IntegerField +from flask_wtf.file import FileField +from wtforms.validators import DataRequired, optional, length, InputRequired, EqualTo + +class NewMeeting(FlaskForm): + date = DateField('Meeting Date', validators=[DataRequired()]) + time = TimeField('Meeting Time', validators=[DataRequired()]) + location = StringField('Meeting Link', validators=[DataRequired()]) + expected_end = IntegerField('Expected Duration',default=60) + meeting_description = StringField('Meeting Description') + schedule_meeting = SubmitField('Schedule Meeting') + +class UpdateMeeting(FlaskForm): + date = DateField('Meeting Date') + time = TimeField('Meeting Time') + location = StringField('Meeting Link') + expected_end = IntegerField('Expected Duration', default=60) + meeting_description = StringField('Meeting Description') + update_meeting = SubmitField('Update Meeting') + +class NewFileUpload(FlaskForm): + document = FileField('file') + submit_file = SubmitField('Submit File') diff --git a/app/meetings/meeting.py b/app/meetings/meeting.py diff --git a/app/meetings/routes.py b/app/meetings/routes.py @@ -0,0 +1,225 @@ +from app import app +from flask_pymongo import PyMongo +from flask import render_template, redirect, url_for, flash, request +from flask_login import login_required +from app.meetings import bp +from bson.objectid import ObjectId +import datetime, hashlib +import os +from app.meetings.forms import NewMeeting, UpdateMeeting, NewFileUpload +#from app.meetings.update import +#from app.meetings.meeting import + +mongo = PyMongo(app) + +### Define fetch_meeting ### +#TRY TO MOVE TO app.meetings.meeting.py +class MeetingNotFoundError(Exception): + pass + +def fetch_meeting(meeting_id): + meeting = mongo.db.meeting_collection.find_one({"meeting_id":meeting_id}) + + if meeting == None: + raise MeetingNotFoundError(f'Meeting Id {meeting_id} returned None') + else: + return meeting + +### BEGIN DEV ROUTES ### + +@bp.route('/meetings/seed',methods=["GET","PUT"]) +@login_required +def meetingSeed(): + expectedtime = datetime.datetime.now() + expectedtime1 = expectedtime + datetime.timedelta(minutes=60) + expectedtime2 = expectedtime - datetime.timedelta(minutes=60) + r1 = hashlib.sha256(os.urandom(16)).hexdigest() + r2 = hashlib.sha256(os.urandom(16)).hexdigest() + seeds = [ + { + "meeting_id": r1, + "timestamp": datetime.datetime.now(), + "expected_end": expectedtime1, + "location": "https://www.brennen.work", + "documents":["/uploads/test.txt","/uploads/test2.pdf"], + "meeting_description":"Design Meeting" + }, + { + "meeting_id": r2, + "timestamp": datetime.datetime.now(), + "expected_end": expectedtime2, + "location": "https://www.brennen.work", + "documents":["/uploads/test2.doc"], + "minutes_file":"minutes.doc", + "meeting_description":"Design Meet" + } + ] + mongo.db.meeting_collection.delete_many({}) + mongo.db.meeting_collection.insert_many(seeds) + dev_meetings = mongo.db.meeting_collection.find() + return render_template('dev.html',dev_meetings=dev_meetings) + +@bp.route('/meetings/dev',methods=["GET"]) +@login_required +def allMeetings(): + dev_meetings = mongo.db.meeting_collection.find({}) + return render_template('dev.html',dev_meetings=dev_meetings) + +### END DEV ROUTES ### + +#### BEGIN ROUTES #### + +@bp.route('/meetings',methods=["GET"]) +@bp.route('/meetings/',methods=["GET"]) +@login_required +def meetings(): + ongoingMeetings = mongo.db.meeting_collection.aggregate( [ + { + '$match':{ + 'timestamp':{ + '$lt':datetime.datetime.now() + }, + 'expected_end':{ + '$gt':datetime.datetime.now() + } + } + }, + { '$sort':{'timestamp':1}} + ] ) + + upcomingMeetings = mongo.db.meeting_collection.aggregate( [ + { + '$match':{'timestamp':{'$gt': datetime.datetime.now() }} + }, + { + '$sort':{'timestamp':1} + } + ] ) + + pastMeetings = mongo.db.meeting_collection.aggregate( [ + { + '$match':{ 'timestamp': {"$lt": datetime.datetime.now()},'expected_end':{'$lt': datetime.datetime.now()} } + }, + { + '$sort':{ 'timestamp':-1 } + }, + { + '$limit':7 + } + ]) + return render_template('meetings.html',ongoingMeetings=ongoingMeetings,upcomingMeetings=upcomingMeetings,pastMeetings=pastMeetings) + +@bp.route('/meetings/past',methods=["GET"]) +@login_required +def past(): + allOldMeetings = True + pastMeetings = mongo.db.meeting_collection.aggregate( [ + { + '$match':{'expected_end': {"$lt": datetime.datetime.now()} } + }, + { + '$sort':{ 'timestamp':-1 } + } + ] ) + return render_template('meetings.html',pastMeetings=pastMeetings, allOldMeetings=allOldMeetings) + +@bp.route('/meeting/<meeting_id>',methods=["GET"]) +@bp.route('/meeting/<meeting_id>/',methods=["GET"]) +def meeting(meeting_id): + try: + meeting = fetch_meeting(meeting_id) + except MeetingNotFoundError as e: + return render_template('error.html',error=e) + else: + return render_template('meeting.html',meeting=meeting) + +@bp.route('/meeting/new',methods=["GET","POST"]) +@login_required +def new(): + form = NewMeeting() + if form.validate_on_submit(): + m_id = hashlib.sha256(os.urandom(16)).hexdigest() + timestp = datetime.datetime.combine(form.date.data,form.time.data) + expectedtime = timestp + datetime.timedelta(minutes=form.expected_end.data) + try: + new_meeting = {'meeting_id':m_id,'timestamp':timestp,'location':form.location.data,'expected_end':expectedtime} + if form.meeting_description.data != "": + new_meeting['meeting_description']=form.meeting_description.data + except Exception: + return "Error assigning form data" + else: + mongo.db.meeting_collection.insert_one(new_meeting) + flash("Created new meeting!") + return redirect(url_for('meetings.meetings')) + +# if form.name.data != "": +# new_meeting['meeting_name']=form.name.data + return render_template('new.html',form=form) + +@bp.route('/meeting/<meeting_id>/upload',methods=["GET","POST"]) +@login_required +def upload(meeting_id): + try: + meeting = fetch_meeting(meeting_id) + except MeetingNotFoundError as e: + return render_template('error.html',error=e) + else: + form = NewFileUpload() + upload = datetime.datetime.utcnow() + if form.validate_on_submit(): + flash("submitted") + mongo.db.meeting_collection.update_one({'_id':meeting['_id']},{'$push':{'documents':'brennen'}}) + return redirect(url_for('meetings.upload',meeting_id=meeting['meeting_id'])) + return render_template('upload.html',meeting=meeting,form=form,upload=upload) + +@bp.route('/meeting/<meeting_id>/update',methods=["GET","POST"]) +@login_required +def update(meeting_id): + try: + meeting = fetch_meeting(meeting_id) + except MeetingNotFoundError as e: + return render_template('error.html',error=e) + else: + return render_template('update.html',meeting=meeting) + +@bp.route('/meeting/<meeting_id>/<update>',methods=["GET","POST"]) +@login_required +def change(meeting_id,update): + form = UpdateMeeting() + try: + meeting = fetch_meeting(meeting_id) + except MeetingNotFoundError as e: + return render_template('error.html',error=e) + else: + if form.validate_on_submit(): + if update == "date": + timestamp = datetime.datetime.combine(form.date.data,meeting['timestamp'].time()) + mongo.db.meeting_collection.update_one({'meeting_id':meeting['meeting_id']},{'$set':{'timestamp':timestamp}}) + flash("Updated Date from {} to {}".format(meeting['timestamp'],timestamp)) + return redirect(url_for('meetings.update',meeting_id=meeting['meeting_id'])) + if update == "time": + timestamp = datetime.datetime.combine(meeting['timestamp'].date(),form.time.data) + mongo.db.meeting_collection.update_one({'meeting_id':meeting['meeting_id']},{'$set':{'timestamp':timestamp}}) + flash("Updated Time from {} to {}".format(meeting['timestamp'],timestamp)) + return redirect(url_for('meetings.update',meeting_id=meeting['meeting_id'])) + if update == "location": + mongo.db.meeting_collection.update_one({'meeting_id':meeting['meeting_id']},{'$set':{'location':form.location.data}}) + flash("Updated location from {} to {}".format(meeting['location'],form.location.data)) + return redirect(url_for('meetings.update',meeting_id=meeting['meeting_id'])) + if update == "description": + mongo.db.meeting_collection.update_one({'meeting_id':meeting['meeting_id']},{'$set':{'meeting_description':form.meeting_description.data}}) + flash("Updated description to {}".format(form.meeting_description.data)) + return redirect(url_for('meetings.update',meeting_id=meeting['meeting_id'])) + return render_template('form.html',meeting=meeting,update=update,form=form) + +@bp.route('/meeting/<meeting_id>/remove',methods=["GET","POST"]) +@login_required +def remove(meeting_id): + try: + meeting = fetch_meeting(meeting_id) + except MeetingNotFoundError as e: + return render_template('error.html',error=e) + else: + mongo.db.meeting_collection.delete_one(meeting) + flash("Deleted meeting {}".format(meeting['meeting_description'])) + return redirect(url_for('meetings.meetings')) diff --git a/app/meetings/templates/dev.html b/app/meetings/templates/dev.html @@ -0,0 +1,13 @@ +{% extends 'base.html' %} + +{% block title %}DEV Seed Meeting{% endblock %} + +{% block content %} + + {%- for x in dev_meetings %} + {%- print(x) %} + </br> + </br> + {%- endfor %} + +{% endblock %} diff --git a/app/meetings/templates/error.html b/app/meetings/templates/error.html @@ -0,0 +1,12 @@ +{% extends 'base.html' %} + +{% block title %}Error{% endblock %} + +{% block content %} + +<div style="text-align:center;"> + <h3>{{ error }}</h3> + <a href="{{ url_for('meetings.meetings') }}">back to meetings</a> +</div> + +{% endblock %} diff --git a/app/meetings/templates/form.html b/app/meetings/templates/form.html @@ -0,0 +1,22 @@ +{% extends 'base.html' %} + +{% block title %}Update Meeting{% endblock %} + +{% block content %} +<section> + <a href="{{ url_for('meetings.update',meeting_id=meeting['meeting_id']) }}">back to meetings</a> + <h3>Update {{meeting['timestamp'].date() }} Meeting</h3> +<form action="" method="POST" novalidate> + {{ form.hidden_tag() }} + {% for error in form.errors %} + <span style="color:red;">[{{ error }}]</span> + {% endfor %} + {% if update == "date" %}{{ form.date.label }}{{ form.date() }}{% endif %} + {% if update == "time" %}{{ form.time.label }}{{ form.time() }}{% endif %} + {% if update == "end" %}{{ form.expected_end.label }}{{ form.expected_end() }}{% endif %} + {% if update == "location" %}{{ form.location.label }}{{ form.location() }}{% endif %} + {% if update == "description" %}{{ form.meeting_description.label }}{{ form.meeting_description() }}{% endif %} + {{ form.update_meeting() }} +</form> +</section> +{% endblock %} diff --git a/app/meetings/templates/meeting.html b/app/meetings/templates/meeting.html @@ -0,0 +1,23 @@ +{% extends 'base.html' %} + +{% block title %}{{ meeting.timestamp.date() }} Meeting{% endblock %} + +{% block content %} + {% if meeting %} + <section class="meeting"> + <a href="{{ url_for('meetings.meetings') }}">back to meetings</a> + <h2 style="color:red;align:left">{{ meeting.timestamp.date() }}</h2><h3 style="align:right">{{ meeting.timestamp.time().isoformat(timespec='minutes') }}</h3> + <a href="{{ meeting.location }}">@ {{ meeting.location }}</a> + <div><p>{{ meeting.meeting_description }}</p></div> + <div><h4 href="color:red">Documents:</h4><a href="{{ url_for('meetings.upload',meeting_id=meeting['meeting_id']) }}">[upload]</a></div> + {% if meeting.documents %} + {% for document in meeting['documents'] %} + <div> + <a href="">{{ document }}</a> + </div> + {% endfor %} + {% endif %} + <a href="{{ url_for('meetings.update',meeting_id=meeting['meeting_id']) }}">modify</a> + </section> + {% endif %} +{% endblock %} diff --git a/app/meetings/templates/meetings.html b/app/meetings/templates/meetings.html @@ -0,0 +1,49 @@ +{% extends 'base.html' %} + +{% block title %}Meetings{% endblock %} + +{% block content %} + {% with messages = get_flashed_messages() %} + {% if messages %} + {% for message in messages %} + <div id="messagebanner"><p>{{ message }}</p></div> + {% endfor %} + {% endif %} + {% endwith %} + <section> + {% if ongoingMeetings %} + <section class="ongoing"> + <h2 style="color:red">Ongoing</h2> + {% for ongoing in ongoingMeetings %} + <a href="{{ url_for('meetings.meeting',meeting_id=ongoing['meeting_id']) }}"><div class="button-box">{{ ongoing.timestamp.date().isoformat() }} <a style="color:red" href="{{ ongoing['location'] }}" target="_blank"><div class="button">Join</div></a></div> + <p>{{ ongoing['meeting_description'] }} </p> + <p>{{ ongoing['expected_end'].time().isoformat(timespec='minutes') }}</p> + </a> + {% endfor %} + </section> + {% endif %} + {% if upcomingMeetings %} + <section class="upcoming"> + <h2>Upcoming:</h2> + {%- for upcoming in upcomingMeetings %} + <a href="{{ url_for('meetings.meeting',meeting_id=upcoming['meeting_id']) }}"><div>{{ upcoming.timestamp.date().isoformat() }} @ {{ upcoming.timestamp.time().isoformat(timespec='minutes') }}</div> + <p>{{ upcoming['meeting_description'] }}</p> + </a> + {%- endfor %} + </section> + {% endif %} + {% if pastMeetings %} + <section> + <h2>Past Meetings:</h2> + {%- for meeting in pastMeetings %} + <div> + <a href="{{ url_for('meetings.meeting',meeting_id=meeting['meeting_id']) }}"><div>{{ meeting.timestamp.date().isoformat() }} @ {{ meeting.timestamp.time().isoformat(timespec='minutes') }}</div>{% if allOldMeetings %}<div>{{meeting['meeting_description']}}</div>{% endif %}</a> + </div> + {%- endfor %} + {% if not allOldMeetings %}<a href="{{ url_for('meetings.past') }}">View older meetings</a>{% endif %} + {% if allOldMeetings %}<a href="{{ url_for('meetings.meetings') }}">Back to meetings</a>{% endif %} + </section> + {% endif %} + <a href="{{ url_for('meetings.new') }}">Schedule Meeting</a> + </section> +{% endblock %} diff --git a/app/meetings/templates/new.html b/app/meetings/templates/new.html @@ -0,0 +1,22 @@ +{% extends 'base.html' %} + +{% block title %}New Meeting{% endblock %} + +{% block content %} +<section> + <a href="{{ url_for('meetings.meetings') }}">back to meetings</a> +<h3>Schedule new Meeting</h3> +<form action="" method="POST" novalidate> + {{ form.hidden_tag() }} + {% for error in form.errors %} + <span style="color:red;">[{{ error }}]</span> + {% endfor %} + {{ form.date.label }}{{ form.date() }} + {{ form.time.label }}{{ form.time() }} + {{ form.expected_end.label }}{{ form.expected_end() }} + {{ form.location.label }}{{ form.location() }} + {{ form.meeting_description.label }}{{ form.meeting_description() }} + {{ form.schedule_meeting() }} +</form> +</section> +{% endblock %} diff --git a/app/meetings/templates/update.html b/app/meetings/templates/update.html @@ -0,0 +1,32 @@ +{% extends 'base.html' %} + +{% block title %}{{ meeting.date }}Update Meeting{% endblock %} + +{% block content %} + {% with messages = get_flashed_messages() %} + {% if messages %} + {% for message in messages %} + <div id="messagebanner"><p>{{ message }}</p></div> + {% endfor %} + {% endif %} + {% endwith %} + {% if meeting %} + <section class="meeting"> + <a href="{{ url_for('meetings.meeting',meeting_id=meeting['meeting_id']) }}">back to meeting</a> + <div style="display:flex"><h2>Date: {{ meeting.timestamp.date() }}</h2><a href="{{url_for('meetings.change',meeting_id=meeting["meeting_id"],update="date")}}"style="color:red">change</a></div> + <div style="display:flex"><h3>Time: {{ meeting.timestamp.time().isoformat(timespec="minutes") }}</h3><a href="{{url_for('meetings.change',meeting_id=meeting['meeting_id'],update="time")}}"style="color:red">change</a></div> + <div style="display:flex"><h3>Location link: {{ meeting.location }}</h3><a href="{{url_for('meetings.change',meeting_id=meeting['meeting_id'],update="location")}}"style="color:red">change</a></div> + <div style="display:flex"><h3>Description: {{ meeting.meeting_destription }}</h3><a href="{{url_for('meetings.change',meeting_id=meeting['meeting_id'],update="description")}}"style="color:red">change</a></div> + <div style="display:flex"><h4 href="color:red">Documents:</h4><a href="{{ url_for('meetings.upload',meeting_id=meeting['meeting_id']) }}" style="color:red">[upload]</a></div> + {% if meeting.document %} + {% for document in meeting.document %} + <div> + <a href="">{{ document }}</a><a href=""style="color:red">remove</a> + </div> + {% endfor %} + {% endif %} + <a href="{{ url_for('meetings.meeting',meeting_id=meeting['meeting_id']) }}">back to meeting</a> + <a href="{{ url_for('meetings.remove',meeting_id=meeting['meeting_id']) }}">remove meeting</a> + </section> + {% endif %} +{% endblock %} diff --git a/app/meetings/templates/upload.html b/app/meetings/templates/upload.html @@ -0,0 +1,29 @@ +{% extends 'base.html' %} + +{% block title %}Upload{% endblock %} + +{% block content %} +{% with messages = get_flashed_messages() %} +{% if messages %} + {% for message in messages %} + <div id="messagebanner"><p>{{ message }}</p></div> + {% endfor %} +{% endif %} +{% endwith %} + <a href="{{ url_for('meetings.meeting',meeting_id=meeting['meeting_id']) }}">back to meeting</a> + <section class="meeting"> + <h2>{{ upload.date() }} {{ upload.time().isoformat(timespec='minutes') }}</h2> + <p>Upload document to {{ meeting['timestamp'].date() }} Meeting for {{ meeting['meeting_description'] }}</p> + <form action="" method="POST" novalidate enctype="multipart/form-data"> + {{ form.hidden_tag() }} + {{ form.document() }} + {{ form.submit_file() }} + </form> + {% if meeting.documents %} + <h2>existing documents</h2> + {% for doc in meeting['documents'] %} + <p>{{ doc }}</p> + {% endfor %} + {% endif %} + </section> +{% endblock %} diff --git a/app/meetings/update.py b/app/meetings/update.py