Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qr code scanner #465

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion hackathon_site/event/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from import_export import resources
from import_export.admin import ExportMixin

from event.models import Profile, Team as EventTeam, User
from event.models import Profile, Team as EventTeam, User, UserActivity
from hardware.admin import OrderInline

admin.site.unregister(User)
Expand Down Expand Up @@ -96,5 +96,28 @@ def get_members_count(self, obj):
return obj.members_count


@admin.register(UserActivity)
class UserActivityAdmin(ExportMixin, admin.ModelAdmin):
list_display = (
"get_user_name",
"sign_in",
"lunch1",
"dinner1",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can make this as dynamic by setting it to the variable in the setting file.

"breakfast2",
"lunch2",
)

def get_user_name(self, obj):
return f"{obj.user.first_name} {obj.user.last_name}"

get_user_name.short_description = "Name"

def get_queryset(self, request):
return super().get_queryset(request).select_related("user")

def get_export_queryset(self, request):
return super().get_queryset(request).select_related("user")


# Register your models here.
admin.site.register(Profile)
127 changes: 127 additions & 0 deletions hackathon_site/event/jinja2/event/admin_qr_scanner.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
{% extends "event/base.html" %}

{% block nav_links %}
<li><a href="{{ url("event:dashboard") }}" class="active">Dashboard</a></li>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the class=active

<li><a href="{{ url("event:change_password") }}">Change Password</a></li>
{% endblock %}

{% block body %}
<div class="backgroundColorDashboard">
<div class="container">
<div class="section">
<div class="borderTopDiv z-depth-3">

{% if get_messages(request) %}
{% for message in get_messages(request) %}
<p id="submitMessage" class="banner banner{{ message.tags }}"> {{ message }} </p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to change the design a bit. It is not clear right now.
CleanShot 2023-02-03 at 17 26 49@2x

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Leo6Leo you need to compile the scss, run yarn run scss in the hackthon_site directory and python manage.py collectstatic

{% endfor %}
{% endif %}

<h1 class="formH1">QR Scanner for Sign-In</h1>
{% if get_curr_sign_in_time(true) %}
<p class="banner bannerinfo"> Current sign in event: <b>{{ get_curr_sign_in_time(true) }}</b> </p>
{% else %}
<p class="banner bannerwarning"> There is currently no event to sign in </p>
{% endif %}

<video id="scanner" style="width: 100%; max-height: 250px; margin: 10px auto"></video>

<br/>

{% if not sign_in_form %}
<h4 class="errorText" style="text-align: center"> {{ hackathon_name }} is not happening now </h4>
{% else %}
<div id="studentInfo">
<h2 class="formH1">Student Information</h2>
<form method="post">
<table>
<tr>
<td>Name</td>
<td id="studentName"></td>
</tr>
<tr>
<td>Email</td>
<td>
{{ csrf_input }}
<div class="input-field">
{{ sign_in_form.email }}

{% if sign_in_form.email.errors %}
<span class="formFieldError">
{% for error in sign_in_form.email.errors %}
{{ error }}
<br />
{% endfor %}
</span>
{% endif %}
</div>
</td>
</tr>
</table>
<button class="btn-large waves-effect waves-light colorBtn" style="margin-top: 15px"
type="submit" id="signInButton">
Sign-In
</button>
</form>
</div>
{% endif %}

<br/>

<table>
<tr>
<th>Event</th>
<th>Time</th>
<th>Sign In Interval</th>
</tr>
{% for event in sign_in_times %}
<tr>
<td> {{ event.description }} </td>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be more intuitive to show which event is happening right now by changing the color of the column or add one more column called status, and show the emoji to indicate the status of the event.

<td> {{ event.time.strftime("%H:%M, %b %d") }} </td>
<td> {{ get_sign_in_interval(event.time) }} </td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div>
</div>
{% endblock %}

{% block scripts %}
<script src="{{ static("event/js/qr-scanner.umd.min.js") }}"></script>
<script type="text/javascript">
const videoElem = document.getElementById("scanner");
let oldData = '';

$("#signInButton").click(() => {
console.log(oldData.split(';'))
});

const qrScanner = new QrScanner(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we able to have a button to disable the camera / QR scanner if we want to use the real scanner instead of the camera.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain this more? What is a "real scanner" I thought QR codes can only be scanned through camera/images.

videoElem,
(result) => {
if (result) {
if (result.data !== oldData) {
data = result.data.split(";");
$("#studentName").text(`${data[0]} ${data[1]}`);
$("#id_email").val(data[2]);
$("#studentInfo").show();
$("#submitMessage").text('')
oldData = result.data;
}
} else {
oldData = '';
$("#studentInfo").hide();
}
},
{
onDecodeError: (error) => {},
highlightScanRegion: true,
highlightCodeOutline: true,
},
);
qrScanner.setInversionMode('both');
qrScanner.start();
</script>
{% endblock %}
66 changes: 66 additions & 0 deletions hackathon_site/event/jinja2/event/dashboard_admin.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{% extends "event/base.html" %}

{% block nav_links %}
<li><a href="{{ url("event:dashboard") }}" class="active">Dashboard</a></li>
<li><a href="{{ url("event:change_password") }}">Change Password</a></li>
{% endblock %}

{% block body %}
<div class="backgroundColorDashboard">
<div class="container">
<div class="section">
<div class="borderTopDiv z-depth-3">
<h1 class="formH1">Admin Dashboard</h1>

<h2 class="formH2">Administrative Actions</h2>
<div class="btn-group-sm">
<a href="{{ url("event:qr-scanner") }}" class="btn-sm btn-small waves-effect waves-light colorBtn">
Scan QR Code
</a>
<a href="" class="btn-sm btn-small waves-effect waves-light colorBtn">
Review Applications
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Link waiting to be updated

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should hide these to all our volunteers. We don't want everyone to see our sensitive data.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll disable those buttons for now (since reviews will be over at that point) and we can figure out later all the volunteer permissions.

</a>
<a href="" class="btn-sm btn-small waves-effect waves-light colorBtn">
View Applications
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Link waiting to be updated

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should hide these to all our volunteers. We don't want everyone to see our sensitive data.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as above.

</a>
</div>

<br/>

<h2 class="formH2">Export Data to Google Sheets</h2>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should hide these to all our volunteers. We don't want everyone to see our sensitive data.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, they're dsiabled for now

<p>Clicking any of the below buttons will export the respective data to google sheets in this folder. Files will not be replaced, a new file will be created any time new data is exported.</p>
<p>(Work In Progress)</p>
<br/>
<div class="btn-group-sm">
<button disabled class="btn-sm btn-small waves-effect waves-light colorBtn"
type="submit">User Data</button>
<button disabled class="btn-sm btn-small waves-effect waves-light colorBtn"
type="submit">Application Data</button>
<button disabled class="btn-sm btn-small waves-effect waves-light colorBtn"
type="submit">Review Data</button>
<button disabled class="btn-sm btn-small waves-effect waves-light colorBtn"
type="submit">Sign-In Data</button>
</div>
</div>

<div class="borderTopDiv z-depth-3">
<h1 class="formH1">Analytics</h1>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should hide these to all our volunteers. We don't want everyone to see our sensitive data.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

smae as comment above

<table>
<tr>
<td>Total number of sign-ups:</td>
<td>345</td>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just don't forget to use the dynamic data.

</tr>
<tr>
<td>Total number of completed applications:</td>
<td>215</td>
</tr>
<tr>
<td>Total number of completed reviews:</td>
<td>0</td>
</tr>
</table>
</div>
</div>
</div>
</div>
{% endblock %}
15 changes: 15 additions & 0 deletions hackathon_site/event/jinja2/event/dashboard_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ <h4 class="formH2"><b>Congratulations!</b> You've been accepted into {{ hackatho
{% if (application.rsvp and using_rsvp) or not using_rsvp %}
<p>Make sure you read the <a class="primaryText hoverLink" href="{{ participant_package_link }}" rel="noopener" target="_blank">participant package</a> for all the info regarding
the event, and join our <a class="primaryText hoverLink" href="{{ chat_room_link }}" rel="noopener" target="_blank">{{ chat_room_name }}</a>. Stay tuned for more updates regarding detailed event logistics, and we hope to see you soon!</p> <br />
<div style="margin-bottom: 15px; display: flex; flex-direction: column; align-items: center">
<p>Please show the QR code below to the front desk to sign-in. .</p>
<canvas id="qrcode" style="width: 150px; height: 150px; margin: 10px auto"></canvas>
</div>
{% endif %}
<p>If you have questions, read the <a class="primaryText hoverLink" href="#faq">FAQ</a>, or feel free to contact us.</p> <br />

Expand Down Expand Up @@ -226,4 +230,15 @@ <h2 class="formH2" id="faq">Application FAQs</h2>
</div>
</div>
</div>
{% endblock %}

{% block scripts %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrious/4.0.2/qrious.min.js"></script>
<script type="text/javascript">
var qrcode = new QRious({
element: document.getElementById("qrcode"),
value: "{{ user.first_name }};{{ user.last_name }};{{ user.email }}",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is unsafe to do so. Here we are exposing user's first name, last name, and their email directly in the QR code. I would recommend we encrypt it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So for newhacks I think data was encoded the same way in the qr code. But also, I don't think this is unsafe because only the participant will have their qr code and first/last name and email is not sensitive data (in my opinion but let me know why you think it is). I don't see how people's information will be exposed in any way. Also encryption on the frontend is not super useful because ppl can find out the encryption algorithm and decrypt it.

size: 150,
});
</script>
{% endblock %}
42 changes: 42 additions & 0 deletions hackathon_site/event/migrations/0008_useractivity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Generated by Django 3.2.12 on 2023-01-12 21:58

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("event", "0007_hss_test_users"),
]

operations = [
migrations.CreateModel(
name="UserActivity",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("sign_in", models.DateTimeField(null=True)),
("lunch1", models.DateTimeField(null=True)),
("dinner1", models.DateTimeField(null=True)),
("breakfast2", models.DateTimeField(null=True)),
("lunch2", models.DateTimeField(null=True)),
(
"user",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
to=settings.AUTH_USER_MODEL,
),
),
],
),
]
9 changes: 9 additions & 0 deletions hackathon_site/event/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,12 @@ def save(self, *args, **kwargs):

def __str__(self):
return f"{self.id} | {self.user.first_name} {self.user.last_name}"


class UserActivity(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
sign_in = models.DateTimeField(null=True)
lunch1 = models.DateTimeField(null=True)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any ways to make this dynamic? My suggestion on this design would be: We have the table called UserActivity, and the attribute for the table will be user, created_time, event(foreign key to the table Event Arrangement), scanner(Foreign key to the table User). And another table called event, storing all the events that the admin created. And it will potentially have these attributes: event name, event id, start_time, end_time, discord_bot_notify(boolean, if it is true, discord bot will send an announcement in the channel), and more....

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'll think about it. My main idea was because we will have a known finite number of events so in the future we can set it up just like how we set up the application model questions, but if you want to extend it to further uses I can change it.

dinner1 = models.DateTimeField(null=True)
breakfast2 = models.DateTimeField(null=True)
lunch2 = models.DateTimeField(null=True)
Loading