Skip to content

Commit d0240d1

Browse files
authored
Merge pull request #33 from wpi-acm/event-date-time-fields
Event Modals & Split date/time fields
2 parents 3a41dc6 + f5eb90b commit d0240d1

File tree

7 files changed

+264
-21
lines changed

7 files changed

+264
-21
lines changed

goathacks/admin/events.py

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import flask
2-
from flask import Response, render_template, redirect, request, url_for, flash
2+
from flask import Response, render_template, redirect, request, url_for, flash, current_app
33
from flask_login import current_user, login_required
44
from goathacks.admin import bp, forms
55
from goathacks import db
66
from goathacks.models import Event
77

8-
import io, qrcode
8+
import io, qrcode, datetime
99
import qrcode.image.pure
1010

1111
@bp.route("/events")
@@ -16,7 +16,86 @@ def list_events():
1616

1717
events = Event.query.all()
1818

19-
return render_template("events/list.html", events=events)
19+
form = forms.EventForm()
20+
21+
return render_template("events/list.html", events=events, form=form)
22+
23+
@bp.route("/event/<int:id>/delete")
24+
@login_required
25+
def delete_event(id):
26+
if not current_user.is_admin:
27+
return {"status": "error", "message": "Unauthorized"}
28+
29+
event = Event.query.filter_by(id=id).first()
30+
31+
if event is None:
32+
return {"status": "error", "message": "Invalid event ID"}
33+
34+
db.session.delete(event)
35+
db.session.commit()
36+
37+
return {"status": "success"}
38+
39+
@bp.route("/event/<int:id>")
40+
@login_required
41+
def event(id):
42+
if not current_user.is_admin:
43+
return {"status": "error", "message": "Unauthorized"}
44+
45+
event = Event.query.filter_by(id=id).first()
46+
47+
if event is None:
48+
return {"status": "error", "message": "Invalid event ID"}
49+
50+
return event.create_json()
51+
52+
@bp.route("/event/<int:id>", methods=["POST"])
53+
@login_required
54+
def update_create_event(id):
55+
if not current_user.is_admin:
56+
flash("Unauthorized")
57+
return redirect(url_for("dashboard.home"))
58+
59+
name = request.form.get('name')
60+
description = request.form.get('description')
61+
location = request.form.get('location')
62+
start_day = request.form.get('start_day')
63+
start_time = request.form.get('start_time')
64+
end_day = request.form.get('end_day')
65+
end_time = request.form.get('end_time')
66+
start = datetime.datetime.combine(datetime.date.fromisoformat(start_day),
67+
datetime.time.fromisoformat(start_time))
68+
end = datetime.datetime.combine(datetime.date.fromisoformat(end_day),
69+
datetime.time.fromisoformat(end_time))
70+
category = request.form.get("category")
71+
72+
if id == 0:
73+
# new event
74+
e = Event(
75+
name=name,
76+
description=description,
77+
location=location,
78+
start_time=start,
79+
category=category,
80+
end_time=end)
81+
db.session.add(e)
82+
db.session.commit()
83+
current_app.logger.info(f"{current_user} is creating a new event: {e.name}")
84+
else:
85+
e = Event.query.filter_by(id=id).first()
86+
if e is None:
87+
return {"status": "error", "message": "Invalid event ID"}
88+
e.name = name
89+
e.description = description
90+
e.location = location
91+
e.start_time = start
92+
e.end_time = end
93+
e.category=category
94+
db.session.commit()
95+
current_app.logger.info(f"{current_user} is updating an existing event: {e.name}")
96+
97+
98+
return redirect(url_for("admin.list_events"))
2099

21100
@bp.route("/events/events.json")
22101
@login_required

goathacks/admin/forms.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from flask_wtf import FlaskForm
2-
from wtforms import StringField, DateTimeField, SubmitField, TextAreaField
2+
from wtforms import StringField, DateField, TimeField, SubmitField, TextAreaField
33
from wtforms.validators import DataRequired
44

55
class EventForm(FlaskForm):
66
name = StringField("Name", validators=[DataRequired()])
77
description = TextAreaField("Description")
88
location = StringField("Location", validators=[DataRequired()])
9-
start_time = DateTimeField("Start Time", validators=[DataRequired()])
10-
end_time = DateTimeField("End Time", validators=[DataRequired()])
9+
start_day = DateField("Start Day", validators=[DataRequired()])
10+
start_time = TimeField("Start Time", validators=[DataRequired()])
11+
end_day = DateField("End Day", validators=[DataRequired()])
12+
end_time = TimeField("End Time", validators=[DataRequired()])
1113
category = StringField("Category")
1214
submit = SubmitField("Submit")

goathacks/models.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class User(db.Model, UserMixin):
2121
phone = Column(String, nullable=True)
2222
gender = Column(String, nullable=True)
2323

24+
def __str__(self):
25+
return f"{self.first_name} {self.last_name} ({self.email})"
2426
def create_json_output(lis):
2527
hackers = []
2628

@@ -48,7 +50,7 @@ def user_loader(user_id):
4850
@login.unauthorized_handler
4951
def unauth():
5052
flash("Please login first")
51-
return redirect(url_for("registration.register"))
53+
return redirect(url_for("registration.login"))
5254

5355

5456
class PwResetRequest(db.Model):
@@ -73,17 +75,20 @@ def create_json_output(lis):
7375
events = []
7476

7577
for e in lis:
76-
events.append({
77-
'id': e.id,
78-
'name': e.name,
79-
'description': e.description,
80-
'location': e.location,
81-
'start': e.start_time,
82-
'end': e.end_time,
83-
'category': e.category
84-
})
78+
events.append(e.create_json())
8579

8680
return events
81+
82+
def create_json(self):
83+
return {
84+
"id": self.id,
85+
"name": self.name,
86+
"description": self.description,
87+
"location": self.location,
88+
"start_time": self.start_time.isoformat(),
89+
"end_time": self.end_time.isoformat(),
90+
"category": self.category
91+
}
8792

8893
def get_checkins(self):
8994
checkins = EventCheckins.query.filter_by(event_id=self.id).all()

goathacks/static/js/jquery-3.6.3.min.js

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

goathacks/templates/events/list.html

Lines changed: 157 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ <h2>Events</h2>
1919
<th>Category</th>
2020
<th>Checked in</th>
2121
<th>QR Code</th>
22-
<th><a href="{{url_for('admin.new_event')}}">New</a></th>
22+
<th><a href="#editModal" data-bs-toggle="modal" data-id="0">New</a></th>
2323
</tr>
2424
</thead>
2525
<tbody>
@@ -34,11 +34,166 @@ <h2>Events</h2>
3434
<td>{{ event.get_checkins()|length }}</td>
3535
<td><a href='{{ url_for("admin.qrcode_event", id=event.id)
3636
}}'>QR Code</a></td>
37-
<td><a href="{{url_for('admin.edit_event', id=event.id)}}">Edit</a></td>
37+
<td><a href="#editModal" data-bs-toggle="modal" data-id="{{ event.id}}" >Edit</a></td>
3838
</tr>
3939
{% endfor %}
4040
</tbody>
4141
</table>
4242
</div>
4343
</div>
44+
45+
<div class="modal" id="editModal" tabindex="-1" aria-labelledby="editModalLabel"
46+
aria-hidden="true">
47+
<div class="modal-dialog">
48+
<div class="modal-content">
49+
<div class="modal-header">
50+
<h1 class="modal-title fs-5" id="editModalLabel">Event</h1>
51+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
52+
</div>
53+
<form class="form" id="edit-form" action="/admin/events/0" role="form" method="post">
54+
<div class="modal-body">
55+
{{ form.csrf_token }}
56+
<div class="form-floating mb-3 required">
57+
{{ form.name(class="form-control") }}
58+
{{ form.name.label() }}
59+
</div>
60+
<div class="form-floating mb-3">
61+
{{ form.description(class="form-control") }}
62+
{{ form.description.label() }}
63+
</div>
64+
<div class="form-floating mb-3 required">
65+
{{ form.location(class="form-control") }}
66+
{{ form.location.label() }}
67+
</div>
68+
<div class="form-floating mb-3">
69+
{{ form.category(class="form-control") }}
70+
{{ form.category.label() }}
71+
</div>
72+
<div class="row">
73+
<div class="col">
74+
<div class="form-floating mb-3 required">
75+
{{ form.start_day(class="form-control") }}
76+
{{ form.start_day.label() }}
77+
</div>
78+
</div>
79+
<div class="col">
80+
<div class="form-floating mb-3 required">
81+
{{ form.start_time(class="form-control") }}
82+
{{ form.start_time.label() }}
83+
</div>
84+
</div>
85+
</div>
86+
<div class="row">
87+
<div class="col">
88+
<div class="form-floating mb-3 required">
89+
{{ form.end_day(class="form-control") }}
90+
{{ form.end_day.label() }}
91+
</div>
92+
</div>
93+
<div class="col">
94+
<div class="form-floating mb-3 required">
95+
{{ form.end_time(class="form-control") }}
96+
{{ form.end_time.label() }}
97+
</div>
98+
</div>
99+
</div>
100+
</div>
101+
<div class="modal-footer">
102+
<button type="button" class="btn btn-danger" data-id="0" id="delete">Delete</button>
103+
<button type="submit" class="btn btn-primary" id="edit-save">Save changes</button>
104+
</div>
105+
</form>
106+
</div>
107+
</div>
108+
</div>
109+
110+
<script src="{{ url_for('static', filename='js/jquery-3.6.3.min.js') }}" charset="utf-8"></script>
111+
112+
<script charset="utf-8">
113+
const editButton = document.getElementById("edit-save")
114+
115+
$('#delete').on("click", (event) => {
116+
if (window.confirm("Delete this event?")) {
117+
console.log("Got OK")
118+
deleteButton = document.getElementById("delete")
119+
id = deleteButton.dataset.id
120+
$.get(`/admin/event/${id}/delete`, (data) => {
121+
if (data.status == "error") {
122+
window.alert(`Error: ${data.message}`)
123+
} else {
124+
window.alert("Success")
125+
}
126+
location.reload()
127+
})
128+
}
129+
})
130+
131+
$('#editModal').on('show.bs.modal', function(event) {
132+
var modal = $(this)
133+
modal.find('#name').val('')
134+
modal.find('#location').val('')
135+
modal.find('#description').val('')
136+
modal.find('#start_day').val('')
137+
modal.find('#start_time').val('')
138+
modal.find('#end_day').val('')
139+
modal.find('#end_time').val('')
140+
141+
var button = $(event.relatedTarget)
142+
var name,description,loc,start_time,start_day,end_time,end_day
143+
id = button.data('id')
144+
145+
saveButton = document.getElementById("edit-save")
146+
saveButton.dataset.id = id
147+
148+
deleteButton = document.getElementById("delete")
149+
deleteButton.dataset.id = id
150+
151+
editForm = document.getElementById("edit-form")
152+
editForm.action = "/admin/event/" + id
153+
154+
if (id) {
155+
$.get(`/admin/event/${id}`, (data) => {
156+
157+
if (data.status == "error") {
158+
// This is a new event, do nothing!
159+
} else {
160+
name = data.name,
161+
description = data.description,
162+
loc = data.location,
163+
category = data.category
164+
165+
start = new Date(data.start_time)
166+
167+
var day = ("0" + start.getDate()).slice(-2);
168+
var month = ("0" + (start.getMonth() + 1)).slice(-2);
169+
170+
start_day = start.getFullYear()+"-"+(month)+"-"+(day);
171+
start_time = `${start.getHours()}:${padTwoDigits(start.getMinutes())}`
172+
end = new Date(data.end_time)
173+
174+
var day = ("0" + end.getDate()).slice(-2);
175+
var month = ("0" + (end.getMonth() + 1)).slice(-2);
176+
177+
end_day = end.getFullYear()+"-"+(month)+"-"+(day);
178+
end_time = `${end.getHours()}:${padTwoDigits(end.getMinutes())}`
179+
}
180+
181+
modal.find('#name').val(name)
182+
modal.find('#location').val(loc)
183+
modal.find('#description').val(description)
184+
modal.find('#start_day').val(start_day)
185+
modal.find('#start_time').val(start_time)
186+
modal.find('#end_day').val(end_day)
187+
modal.find('#end_time').val(end_time)
188+
modal.find('#category').val(category)
189+
190+
191+
});
192+
}
193+
})
194+
195+
function padTwoDigits(num) {
196+
return num.toString().padStart(2, '0')
197+
}
198+
</script>
44199
{% endblock %}

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Flask-Login==0.6.2
99
Flask-Migrate==4.0.0
1010
Flask-SQLAlchemy==3.0.2
1111
Flask-WTF==1.0.1
12-
greenlet==2.0.1
12+
greenlet
1313
itsdangerous==2.1.2
1414
Jinja2==3.1.2
1515
Mako==1.2.4
@@ -19,7 +19,7 @@ psycopg2==2.9.5
1919
pynvim==0.4.3
2020
python-dotenv==0.21.0
2121
SQLAlchemy==1.4.44
22-
uWSGI==2.0.21
22+
uWSGI
2323
Werkzeug==2.2.2
2424
WTForms==3.0.1
2525
ulid

0 commit comments

Comments
 (0)