diff --git a/hackathon_site/event/admin.py b/hackathon_site/event/admin.py index eadbf4b8c..58a7f703e 100644 --- a/hackathon_site/event/admin.py +++ b/hackathon_site/event/admin.py @@ -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) @@ -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", + "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) diff --git a/hackathon_site/event/jinja2/event/admin_qr_scanner.html b/hackathon_site/event/jinja2/event/admin_qr_scanner.html new file mode 100644 index 000000000..cbf754044 --- /dev/null +++ b/hackathon_site/event/jinja2/event/admin_qr_scanner.html @@ -0,0 +1,127 @@ +{% extends "event/base.html" %} + +{% block nav_links %} +
  • Dashboard
  • +
  • Change Password
  • +{% endblock %} + +{% block body %} +
    +
    +
    +
    + + {% if get_messages(request) %} + {% for message in get_messages(request) %} + + {% endfor %} + {% endif %} + +

    QR Scanner for Sign-In

    + {% if get_curr_sign_in_time(true) %} + + {% else %} + + {% endif %} + + + +
    + + {% if not sign_in_form %} +

    {{ hackathon_name }} is not happening now

    + {% else %} +
    +

    Student Information

    +
    + + + + + + + + + +
    Name
    Email + {{ csrf_input }} +
    + {{ sign_in_form.email }} + + {% if sign_in_form.email.errors %} + + {% for error in sign_in_form.email.errors %} + {{ error }} +
    + {% endfor %} +
    + {% endif %} +
    +
    + +
    +
    + {% endif %} + +
    + + + + + + + + {% for event in sign_in_times %} + + + + + + {% endfor %} +
    EventTimeSign In Interval
    {{ event.description }} {{ event.time.strftime("%H:%M, %b %d") }} {{ get_sign_in_interval(event.time) }}
    +
    +
    +
    +
    +{% endblock %} + +{% block scripts %} + + +{% endblock %} \ No newline at end of file diff --git a/hackathon_site/event/jinja2/event/dashboard_admin.html b/hackathon_site/event/jinja2/event/dashboard_admin.html new file mode 100644 index 000000000..481d6c7cc --- /dev/null +++ b/hackathon_site/event/jinja2/event/dashboard_admin.html @@ -0,0 +1,66 @@ +{% extends "event/base.html" %} + +{% block nav_links %} +
  • Dashboard
  • +
  • Change Password
  • +{% endblock %} + +{% block body %} +
    +
    +
    +
    +

    Admin Dashboard

    + +

    Administrative Actions

    + + +
    + +

    Export Data to Google Sheets

    +

    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.

    +

    (Work In Progress)

    +
    +
    + + + + +
    +
    + +
    +

    Analytics

    + + + + + + + + + + + + + +
    Total number of sign-ups:345
    Total number of completed applications:215
    Total number of completed reviews:0
    +
    +
    +
    +
    +{% endblock %} \ No newline at end of file diff --git a/hackathon_site/event/jinja2/event/dashboard_base.html b/hackathon_site/event/jinja2/event/dashboard_base.html index b1bd98a85..2ac7dbf42 100644 --- a/hackathon_site/event/jinja2/event/dashboard_base.html +++ b/hackathon_site/event/jinja2/event/dashboard_base.html @@ -71,6 +71,10 @@

    Congratulations! You've been accepted into {{ hackatho {% if (application.rsvp and using_rsvp) or not using_rsvp %}

    Make sure you read the participant package for all the info regarding the event, and join our {{ chat_room_name }}. Stay tuned for more updates regarding detailed event logistics, and we hope to see you soon!


    +
    +

    Please show the QR code below to the front desk to sign-in. .

    + +
    {% endif %}

    If you have questions, read the FAQ, or feel free to contact us.


    @@ -226,4 +230,15 @@

    Application FAQs

    +{% endblock %} + +{% block scripts %} + + {% endblock %} \ No newline at end of file diff --git a/hackathon_site/event/migrations/0008_useractivity.py b/hackathon_site/event/migrations/0008_useractivity.py new file mode 100644 index 000000000..6c4c38ff8 --- /dev/null +++ b/hackathon_site/event/migrations/0008_useractivity.py @@ -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, + ), + ), + ], + ), + ] diff --git a/hackathon_site/event/models.py b/hackathon_site/event/models.py index 8ecec5ccd..e745eb1ca 100644 --- a/hackathon_site/event/models.py +++ b/hackathon_site/event/models.py @@ -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) + dinner1 = models.DateTimeField(null=True) + breakfast2 = models.DateTimeField(null=True) + lunch2 = models.DateTimeField(null=True) diff --git a/hackathon_site/event/static/event/js/qr-scanner-worker.min.js b/hackathon_site/event/static/event/js/qr-scanner-worker.min.js new file mode 100644 index 000000000..b31d7f975 --- /dev/null +++ b/hackathon_site/event/static/event/js/qr-scanner-worker.min.js @@ -0,0 +1,106 @@ +export const createWorker = () => + new Worker( + URL.createObjectURL( + new Blob([ + `class x{constructor(a,b){this.width=b;this.height=a.length/b;this.data=a}static createEmpty(a,b){return new x(new Uint8ClampedArray(a*b),a)}get(a,b){return 0>a||a>=this.width||0>b||b>=this.height?!1:!!this.data[b*this.width+a]}set(a,b,c){this.data[b*this.width+a]=c?1:0}setRegion(a,b,c,d,e){for(let f=b;fa||32this.available())throw Error("Cannot read "+a.toString()+" bits");var b=0;if(0>8-c<>b;a-=c;this.bitOffset+=c;8===this.bitOffset&&(this.bitOffset=0,this.byteOffset++)}if(0>c<>c, +this.bitOffset+=a)}return b}available(){return 8*(this.bytes.length-this.byteOffset)-this.bitOffset}}var B,C=B||(B={});C.Numeric="numeric";C.Alphanumeric="alphanumeric";C.Byte="byte";C.Kanji="kanji";C.ECI="eci";C.StructuredAppend="structuredappend";var D,E=D||(D={});E[E.Terminator=0]="Terminator";E[E.Numeric=1]="Numeric";E[E.Alphanumeric=2]="Alphanumeric";E[E.Byte=4]="Byte";E[E.Kanji=8]="Kanji";E[E.ECI=7]="ECI";E[E.StructuredAppend=3]="StructuredAppend";let F="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".split(""); +function ca(a,b){let c=[],d="";b=a.readBits([8,16,16][b]);for(let e=0;e\`%\${("0"+e.toString(16)).substr(-2)}\`).join(""))}catch(e){}return{bytes:c,text:d}} +function da(a,b){a=new ba(a);let c=9>=b?0:26>=b?1:2;for(b={text:"",bytes:[],chunks:[],version:b};4<=a.available();){var d=a.readBits(4);if(d===D.Terminator)return b;if(d===D.ECI)0===a.readBits(1)?b.chunks.push({type:B.ECI,assignmentNumber:a.readBits(7)}):0===a.readBits(1)?b.chunks.push({type:B.ECI,assignmentNumber:a.readBits(14)}):0===a.readBits(1)?b.chunks.push({type:B.ECI,assignmentNumber:a.readBits(21)}):b.chunks.push({type:B.ECI,assignmentNumber:-1});else if(d===D.Numeric){var e=a,f=[];d="";for(var g= +e.readBits([10,12,14][c]);3<=g;){var h=e.readBits(10);if(1E3<=h)throw Error("Invalid numeric value above 999");var k=Math.floor(h/100),m=Math.floor(h/10)%10;h%=10;f.push(48+k,48+m,48+h);d+=k.toString()+m.toString()+h.toString();g-=3}if(2===g){g=e.readBits(7);if(100<=g)throw Error("Invalid numeric value above 99");e=Math.floor(g/10);g%=10;f.push(48+e,48+g);d+=e.toString()+g.toString()}else if(1===g){e=e.readBits(4);if(10<=e)throw Error("Invalid numeric value above 9");f.push(48+e);d+=e.toString()}b.text+= +d;b.bytes.push(...f);b.chunks.push({type:B.Numeric,text:d})}else if(d===D.Alphanumeric){e=a;f=[];d="";for(g=e.readBits([9,11,13][c]);2<=g;)m=e.readBits(11),k=Math.floor(m/45),m%=45,f.push(F[k].charCodeAt(0),F[m].charCodeAt(0)),d+=F[k]+F[m],g-=2;1===g&&(e=e.readBits(6),f.push(F[e].charCodeAt(0)),d+=F[e]);b.text+=d;b.bytes.push(...f);b.chunks.push({type:B.Alphanumeric,text:d})}else if(d===D.Byte)d=ca(a,c),b.text+=d.text,b.bytes.push(...d.bytes),b.chunks.push({type:B.Byte,bytes:d.bytes,text:d.text}); +else if(d===D.Kanji){f=a;d=[];e=f.readBits([8,10,12][c]);for(g=0;gk?k+33088:k+49472,d.push(k>>8,k&255);f=(new TextDecoder("shift-jis")).decode(Uint8Array.from(d));b.text+=f;b.bytes.push(...d);b.chunks.push({type:B.Kanji,bytes:d,text:f})}else d===D.StructuredAppend&&b.chunks.push({type:B.StructuredAppend,currentSequence:a.readBits(4),totalSequence:a.readBits(4),parity:a.readBits(8)})}if(0===a.available()||0===a.readBits(a.available()))return b} +class G{constructor(a,b){if(0===b.length)throw Error("No coefficients.");this.field=a;let c=b.length;if(1a.length&&([b,a]=[a,b]);let c=new Uint8ClampedArray(a.length),d=a.length-b.length;for(var e=0;ea)throw Error("Invalid degree less than 0");if(0===b)return this.field.zero;let c=this.coefficients.length;a=new Uint8ClampedArray(c+a);for(let d=0;d{b^=d}),b;b=this.coefficients[0];for(let d=1;d=this.size&&(a=(a^this.primitive)&this.size-1);for(a=0;aa)throw Error("Invalid monomial degree less than 0");if(0===b)return this.zero;a=new Uint8ClampedArray(a+1);a[0]=b;return new G(this,a)}log(a){if(0===a)throw Error("Can't take log(0)");return this.logTable[a]}exp(a){return this.expTable[a]}} +function fa(a,b,c,d){b.degree()=d/2;){var g=b;let h=e;b=c;e=f;if(b.isZero())return null;c=g;f=a.zero;g=b.getCoefficient(b.degree());for(g=a.inverse(g);c.degree()>=b.degree()&&!c.isZero();){let k=c.degree()-b.degree(),m=a.multiply(c.getCoefficient(c.degree()),g);f=f.addOrSubtract(a.buildMonomial(k,m));c=c.addOrSubtract(b.multiplyByMonomial(k,m))}f=f.multiplyPoly(e).addOrSubtract(h);if(c.degree()>=b.degree())return null}d=f.getCoefficient(0); +if(0===d)return null;a=a.inverse(d);return[f.multiply(a),c.multiply(a)]} +function ha(a,b){let c=new Uint8ClampedArray(a.length);c.set(a);a=new ea(285,256,0);var d=new G(a,c),e=new Uint8ClampedArray(b),f=!1;for(var g=0;gf)return null;c[f]^=d[e]}return c} +let I=[{infoBits:null,versionNumber:1,alignmentPatternCenters:[],errorCorrectionLevels:[{ecCodewordsPerBlock:7,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:10,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:16}]},{ecCodewordsPerBlock:13,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:13}]},{ecCodewordsPerBlock:17,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:9}]}]},{infoBits:null,versionNumber:2,alignmentPatternCenters:[6,18],errorCorrectionLevels:[{ecCodewordsPerBlock:10,ecBlocks:[{numBlocks:1, +dataCodewordsPerBlock:34}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:28}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:22}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:16}]}]},{infoBits:null,versionNumber:3,alignmentPatternCenters:[6,22],errorCorrectionLevels:[{ecCodewordsPerBlock:15,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:55}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:18, +ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:13}]}]},{infoBits:null,versionNumber:4,alignmentPatternCenters:[6,26],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:80}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:32}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:9}]}]}, +{infoBits:null,versionNumber:5,alignmentPatternCenters:[6,30],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:43}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:15},{numBlocks:2,dataCodewordsPerBlock:16}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:11},{numBlocks:2,dataCodewordsPerBlock:12}]}]},{infoBits:null,versionNumber:6,alignmentPatternCenters:[6, +34],errorCorrectionLevels:[{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:68}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:27}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:15}]}]},{infoBits:31892,versionNumber:7,alignmentPatternCenters:[6,22,38],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:78}]},{ecCodewordsPerBlock:18, +ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:31}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:4,dataCodewordsPerBlock:15}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:13},{numBlocks:1,dataCodewordsPerBlock:14}]}]},{infoBits:34236,versionNumber:8,alignmentPatternCenters:[6,24,42],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:97}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:38}, +{numBlocks:2,dataCodewordsPerBlock:39}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:18},{numBlocks:2,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:14},{numBlocks:2,dataCodewordsPerBlock:15}]}]},{infoBits:39577,versionNumber:9,alignmentPatternCenters:[6,26,46],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:36}, +{numBlocks:2,dataCodewordsPerBlock:37}]},{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:16},{numBlocks:4,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:12},{numBlocks:4,dataCodewordsPerBlock:13}]}]},{infoBits:42195,versionNumber:10,alignmentPatternCenters:[6,28,50],errorCorrectionLevels:[{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:68},{numBlocks:2,dataCodewordsPerBlock:69}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4, +dataCodewordsPerBlock:43},{numBlocks:1,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:19},{numBlocks:2,dataCodewordsPerBlock:20}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:15},{numBlocks:2,dataCodewordsPerBlock:16}]}]},{infoBits:48118,versionNumber:11,alignmentPatternCenters:[6,30,54],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:81}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:1, +dataCodewordsPerBlock:50},{numBlocks:4,dataCodewordsPerBlock:51}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:22},{numBlocks:4,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:12},{numBlocks:8,dataCodewordsPerBlock:13}]}]},{infoBits:51042,versionNumber:12,alignmentPatternCenters:[6,32,58],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:92},{numBlocks:2,dataCodewordsPerBlock:93}]}, +{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:36},{numBlocks:2,dataCodewordsPerBlock:37}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:20},{numBlocks:6,dataCodewordsPerBlock:21}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:14},{numBlocks:4,dataCodewordsPerBlock:15}]}]},{infoBits:55367,versionNumber:13,alignmentPatternCenters:[6,34,62],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:107}]}, +{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:37},{numBlocks:1,dataCodewordsPerBlock:38}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:20},{numBlocks:4,dataCodewordsPerBlock:21}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:11},{numBlocks:4,dataCodewordsPerBlock:12}]}]},{infoBits:58893,versionNumber:14,alignmentPatternCenters:[6,26,46,66],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:115}, +{numBlocks:1,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:40},{numBlocks:5,dataCodewordsPerBlock:41}]},{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:16},{numBlocks:5,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:12},{numBlocks:5,dataCodewordsPerBlock:13}]}]},{infoBits:63784,versionNumber:15,alignmentPatternCenters:[6,26,48,70],errorCorrectionLevels:[{ecCodewordsPerBlock:22, +ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:87},{numBlocks:1,dataCodewordsPerBlock:88}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:41},{numBlocks:5,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:24},{numBlocks:7,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:12},{numBlocks:7,dataCodewordsPerBlock:13}]}]},{infoBits:68472,versionNumber:16,alignmentPatternCenters:[6,26,50, +74],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:98},{numBlocks:1,dataCodewordsPerBlock:99}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:45},{numBlocks:3,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:19},{numBlocks:2,dataCodewordsPerBlock:20}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:15},{numBlocks:13,dataCodewordsPerBlock:16}]}]},{infoBits:70749, +versionNumber:17,alignmentPatternCenters:[6,30,54,78],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:107},{numBlocks:5,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:46},{numBlocks:1,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:22},{numBlocks:15,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:17, +dataCodewordsPerBlock:15}]}]},{infoBits:76311,versionNumber:18,alignmentPatternCenters:[6,30,56,82],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:120},{numBlocks:1,dataCodewordsPerBlock:121}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:9,dataCodewordsPerBlock:43},{numBlocks:4,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:22},{numBlocks:1,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2, +dataCodewordsPerBlock:14},{numBlocks:19,dataCodewordsPerBlock:15}]}]},{infoBits:79154,versionNumber:19,alignmentPatternCenters:[6,30,58,86],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:113},{numBlocks:4,dataCodewordsPerBlock:114}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:44},{numBlocks:11,dataCodewordsPerBlock:45}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:21},{numBlocks:4,dataCodewordsPerBlock:22}]}, +{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:9,dataCodewordsPerBlock:13},{numBlocks:16,dataCodewordsPerBlock:14}]}]},{infoBits:84390,versionNumber:20,alignmentPatternCenters:[6,34,62,90],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:107},{numBlocks:5,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:41},{numBlocks:13,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:24}, +{numBlocks:5,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:15},{numBlocks:10,dataCodewordsPerBlock:16}]}]},{infoBits:87683,versionNumber:21,alignmentPatternCenters:[6,28,50,72,94],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:116},{numBlocks:4,dataCodewordsPerBlock:117}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:22}, +{numBlocks:6,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:16},{numBlocks:6,dataCodewordsPerBlock:17}]}]},{infoBits:92361,versionNumber:22,alignmentPatternCenters:[6,26,50,74,98],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:111},{numBlocks:7,dataCodewordsPerBlock:112}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:24}, +{numBlocks:16,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:34,dataCodewordsPerBlock:13}]}]},{infoBits:96236,versionNumber:23,alignmentPatternCenters:[6,30,54,74,102],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:121},{numBlocks:5,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:47},{numBlocks:14,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:24}, +{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:16,dataCodewordsPerBlock:15},{numBlocks:14,dataCodewordsPerBlock:16}]}]},{infoBits:102084,versionNumber:24,alignmentPatternCenters:[6,28,54,80,106],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:117},{numBlocks:4,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:45},{numBlocks:14,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30, +ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:24},{numBlocks:16,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:30,dataCodewordsPerBlock:16},{numBlocks:2,dataCodewordsPerBlock:17}]}]},{infoBits:102881,versionNumber:25,alignmentPatternCenters:[6,32,58,84,110],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:106},{numBlocks:4,dataCodewordsPerBlock:107}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:47},{numBlocks:13, +dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:24},{numBlocks:22,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:15},{numBlocks:13,dataCodewordsPerBlock:16}]}]},{infoBits:110507,versionNumber:26,alignmentPatternCenters:[6,30,58,86,114],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:114},{numBlocks:2,dataCodewordsPerBlock:115}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:19, +dataCodewordsPerBlock:46},{numBlocks:4,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:28,dataCodewordsPerBlock:22},{numBlocks:6,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:33,dataCodewordsPerBlock:16},{numBlocks:4,dataCodewordsPerBlock:17}]}]},{infoBits:110734,versionNumber:27,alignmentPatternCenters:[6,34,62,90,118],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:122},{numBlocks:4,dataCodewordsPerBlock:123}]}, +{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:45},{numBlocks:3,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:23},{numBlocks:26,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:15},{numBlocks:28,dataCodewordsPerBlock:16}]}]},{infoBits:117786,versionNumber:28,alignmentPatternCenters:[6,26,50,74,98,122],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:117}, +{numBlocks:10,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:45},{numBlocks:23,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:24},{numBlocks:31,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:15},{numBlocks:31,dataCodewordsPerBlock:16}]}]},{infoBits:119615,versionNumber:29,alignmentPatternCenters:[6,30,54,78,102,126],errorCorrectionLevels:[{ecCodewordsPerBlock:30, +ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:116},{numBlocks:7,dataCodewordsPerBlock:117}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:21,dataCodewordsPerBlock:45},{numBlocks:7,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:23},{numBlocks:37,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:15},{numBlocks:26,dataCodewordsPerBlock:16}]}]},{infoBits:126325,versionNumber:30,alignmentPatternCenters:[6, +26,52,78,104,130],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:115},{numBlocks:10,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:47},{numBlocks:10,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:24},{numBlocks:25,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:23,dataCodewordsPerBlock:15},{numBlocks:25,dataCodewordsPerBlock:16}]}]}, +{infoBits:127568,versionNumber:31,alignmentPatternCenters:[6,30,56,82,108,134],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:115},{numBlocks:3,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:46},{numBlocks:29,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:42,dataCodewordsPerBlock:24},{numBlocks:1,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:23,dataCodewordsPerBlock:15}, +{numBlocks:28,dataCodewordsPerBlock:16}]}]},{infoBits:133589,versionNumber:32,alignmentPatternCenters:[6,34,60,86,112,138],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:115}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:46},{numBlocks:23,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:24},{numBlocks:35,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19, +dataCodewordsPerBlock:15},{numBlocks:35,dataCodewordsPerBlock:16}]}]},{infoBits:136944,versionNumber:33,alignmentPatternCenters:[6,30,58,86,114,142],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:115},{numBlocks:1,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:14,dataCodewordsPerBlock:46},{numBlocks:21,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:29,dataCodewordsPerBlock:24},{numBlocks:19,dataCodewordsPerBlock:25}]}, +{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:15},{numBlocks:46,dataCodewordsPerBlock:16}]}]},{infoBits:141498,versionNumber:34,alignmentPatternCenters:[6,34,62,90,118,146],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:115},{numBlocks:6,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:14,dataCodewordsPerBlock:46},{numBlocks:23,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:44, +dataCodewordsPerBlock:24},{numBlocks:7,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:59,dataCodewordsPerBlock:16},{numBlocks:1,dataCodewordsPerBlock:17}]}]},{infoBits:145311,versionNumber:35,alignmentPatternCenters:[6,30,54,78,102,126,150],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:121},{numBlocks:7,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:47},{numBlocks:26,dataCodewordsPerBlock:48}]}, +{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:39,dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:15},{numBlocks:41,dataCodewordsPerBlock:16}]}]},{infoBits:150283,versionNumber:36,alignmentPatternCenters:[6,24,50,76,102,128,154],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:121},{numBlocks:14,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6, +dataCodewordsPerBlock:47},{numBlocks:34,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:46,dataCodewordsPerBlock:24},{numBlocks:10,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:15},{numBlocks:64,dataCodewordsPerBlock:16}]}]},{infoBits:152622,versionNumber:37,alignmentPatternCenters:[6,28,54,80,106,132,158],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:122},{numBlocks:4,dataCodewordsPerBlock:123}]}, +{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:29,dataCodewordsPerBlock:46},{numBlocks:14,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:49,dataCodewordsPerBlock:24},{numBlocks:10,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:24,dataCodewordsPerBlock:15},{numBlocks:46,dataCodewordsPerBlock:16}]}]},{infoBits:158308,versionNumber:38,alignmentPatternCenters:[6,32,58,84,110,136,162],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4, +dataCodewordsPerBlock:122},{numBlocks:18,dataCodewordsPerBlock:123}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:46},{numBlocks:32,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:48,dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:42,dataCodewordsPerBlock:15},{numBlocks:32,dataCodewordsPerBlock:16}]}]},{infoBits:161089,versionNumber:39,alignmentPatternCenters:[6,26,54,82,110,138,166], +errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:20,dataCodewordsPerBlock:117},{numBlocks:4,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:40,dataCodewordsPerBlock:47},{numBlocks:7,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:43,dataCodewordsPerBlock:24},{numBlocks:22,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:15},{numBlocks:67,dataCodewordsPerBlock:16}]}]},{infoBits:167017, +versionNumber:40,alignmentPatternCenters:[6,30,58,86,114,142,170],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:118},{numBlocks:6,dataCodewordsPerBlock:119}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:18,dataCodewordsPerBlock:47},{numBlocks:31,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:34,dataCodewordsPerBlock:24},{numBlocks:34,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:20,dataCodewordsPerBlock:15}, +{numBlocks:61,dataCodewordsPerBlock:16}]}]}];function J(a,b){a^=b;for(b=0;a;)b++,a&=a-1;return b}function K(a,b){return b<<1|a} +let ia=[{bits:21522,formatInfo:{errorCorrectionLevel:1,dataMask:0}},{bits:20773,formatInfo:{errorCorrectionLevel:1,dataMask:1}},{bits:24188,formatInfo:{errorCorrectionLevel:1,dataMask:2}},{bits:23371,formatInfo:{errorCorrectionLevel:1,dataMask:3}},{bits:17913,formatInfo:{errorCorrectionLevel:1,dataMask:4}},{bits:16590,formatInfo:{errorCorrectionLevel:1,dataMask:5}},{bits:20375,formatInfo:{errorCorrectionLevel:1,dataMask:6}},{bits:19104,formatInfo:{errorCorrectionLevel:1,dataMask:7}},{bits:30660,formatInfo:{errorCorrectionLevel:0, +dataMask:0}},{bits:29427,formatInfo:{errorCorrectionLevel:0,dataMask:1}},{bits:32170,formatInfo:{errorCorrectionLevel:0,dataMask:2}},{bits:30877,formatInfo:{errorCorrectionLevel:0,dataMask:3}},{bits:26159,formatInfo:{errorCorrectionLevel:0,dataMask:4}},{bits:25368,formatInfo:{errorCorrectionLevel:0,dataMask:5}},{bits:27713,formatInfo:{errorCorrectionLevel:0,dataMask:6}},{bits:26998,formatInfo:{errorCorrectionLevel:0,dataMask:7}},{bits:5769,formatInfo:{errorCorrectionLevel:3,dataMask:0}},{bits:5054, +formatInfo:{errorCorrectionLevel:3,dataMask:1}},{bits:7399,formatInfo:{errorCorrectionLevel:3,dataMask:2}},{bits:6608,formatInfo:{errorCorrectionLevel:3,dataMask:3}},{bits:1890,formatInfo:{errorCorrectionLevel:3,dataMask:4}},{bits:597,formatInfo:{errorCorrectionLevel:3,dataMask:5}},{bits:3340,formatInfo:{errorCorrectionLevel:3,dataMask:6}},{bits:2107,formatInfo:{errorCorrectionLevel:3,dataMask:7}},{bits:13663,formatInfo:{errorCorrectionLevel:2,dataMask:0}},{bits:12392,formatInfo:{errorCorrectionLevel:2, +dataMask:1}},{bits:16177,formatInfo:{errorCorrectionLevel:2,dataMask:2}},{bits:14854,formatInfo:{errorCorrectionLevel:2,dataMask:3}},{bits:9396,formatInfo:{errorCorrectionLevel:2,dataMask:4}},{bits:8579,formatInfo:{errorCorrectionLevel:2,dataMask:5}},{bits:11994,formatInfo:{errorCorrectionLevel:2,dataMask:6}},{bits:11245,formatInfo:{errorCorrectionLevel:2,dataMask:7}}],ja=[a=>0===(a.y+a.x)%2,a=>0===a.y%2,a=>0===a.x%3,a=>0===(a.y+a.x)%3,a=>0===(Math.floor(a.y/2)+Math.floor(a.x/3))%2,a=>0===a.x*a.y% +2+a.x*a.y%3,a=>0===(a.y*a.x%2+a.y*a.x%3)%2,a=>0===((a.y+a.x)%2+a.y*a.x%3)%2]; +function ka(a,b,c){c=ja[c.dataMask];let d=a.height;var e=17+4*b.versionNumber;let f=x.createEmpty(e,e);f.setRegion(0,0,9,9,!0);f.setRegion(e-8,0,8,9,!0);f.setRegion(0,e-8,9,8,!0);for(var g of b.alignmentPatternCenters)for(var h of b.alignmentPatternCenters)6===g&&6===h||6===g&&h===e-7||g===e-7&&6===h||f.setRegion(g-2,h-2,5,5,!0);f.setRegion(6,9,1,e-17,!0);f.setRegion(9,6,e-17,1,!0);6n;n++){let q=k-n;if(!f.get(q,l)){h++;let r=a.get(q,l);c({y:l,x:q})&&(r=!r);g=g<<1|r;8===h&&(b.push(g),g=h=0)}}}e=!e}return b} +function la(a){var b=a.height,c=Math.floor((b-17)/4);if(6>=c)return I[c-1];c=0;for(var d=5;0<=d;d--)for(var e=b-9;e>=b-11;e--)c=K(a.get(e,d),c);d=0;for(e=5;0<=e;e--)for(let g=b-9;g>=b-11;g--)d=K(a.get(e,g),d);a=Infinity;let f;for(let g of I){if(g.infoBits===c||g.infoBits===d)return g;b=J(c,g.infoBits);b=a)return f} +function ma(a){let b=0;for(var c=0;8>=c;c++)6!==c&&(b=K(a.get(c,8),b));for(c=7;0<=c;c--)6!==c&&(b=K(a.get(8,c),b));var d=a.height;c=0;for(var e=d-1;e>=d-7;e--)c=K(a.get(8,e),c);for(e=d-8;e=a?d:null} +function na(a,b,c){let d=b.errorCorrectionLevels[c],e=[],f=0;d.ecBlocks.forEach(h=>{for(let k=0;ke+f.numDataCodewords,0);c=new Uint8ClampedArray(c);a=0;for(let e of d){d=ha(e.codewords,e.codewords.length-e.numDataCodewords);if(!d)return null;for(let f=0;f{const p=g*r+m*u+q;return{x:(e*r+h*u+l)/p,y:(f*r+k*u+n)/p}};for(let r=0;rMath.sqrt(Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2));function O(a){return a.reduce((b,c)=>b+c)} +function qa(a,b,c){let d=N(a,b),e=N(b,c),f=N(a,c),g,h,k;e>=d&&e>=f?[g,h,k]=[b,a,c]:f>=e&&f>=d?[g,h,k]=[a,b,c]:[g,h,k]=[a,c,b];0>(k.x-h.x)*(g.y-h.y)-(k.y-h.y)*(g.x-h.x)&&([g,k]=[k,g]);return{bottomLeft:g,topLeft:h,topRight:k}} +function ra(a,b,c,d){d=(O(P(a,c,d,5))/7+O(P(a,b,d,5))/7+O(P(c,a,d,5))/7+O(P(b,a,d,5))/7)/4;if(1>d)throw Error("Invalid module size");b=Math.round(N(a,b)/d);a=Math.round(N(a,c)/d);a=Math.floor((b+a)/2)+7;switch(a%4){case 0:a++;break;case 2:a--}return{dimension:a,moduleSize:d}} +function Q(a,b,c,d){let e=[{x:Math.floor(a.x),y:Math.floor(a.y)}];var f=Math.abs(b.y-a.y)>Math.abs(b.x-a.x);if(f){var g=Math.floor(a.y);var h=Math.floor(a.x);a=Math.floor(b.y);b=Math.floor(b.x)}else g=Math.floor(a.x),h=Math.floor(a.y),a=Math.floor(b.x),b=Math.floor(b.y);let k=Math.abs(a-g),m=Math.abs(b-h),l=Math.floor(-k/2),n=g{d+=Math.pow(a[f]-e*c,2)});return{averageSize:c,error:d}} +function S(a,b,c){try{let d=P(a,{x:-1,y:a.y},c,b.length),e=P(a,{x:a.x,y:-1},c,b.length),f=P(a,{x:Math.max(0,a.x-a.y)-1,y:Math.max(0,a.y-a.x)-1},c,b.length),g=P(a,{x:Math.min(c.width,a.x+a.y)+1,y:Math.min(c.height,a.y+a.x)+1},c,b.length),h=R(d,b),k=R(e,b),m=R(f,b),l=R(g,b),n=(h.averageSize+k.averageSize+m.averageSize+l.averageSize)/4;return Math.sqrt(h.error*h.error+k.error*k.error+m.error*m.error+l.error*l.error)+(Math.pow(h.averageSize-n,2)+Math.pow(k.averageSize-n,2)+Math.pow(m.averageSize-n,2)+ +Math.pow(l.averageSize-n,2))/n}catch(d){return Infinity}}function T(a,b){for(var c=Math.round(b.x);a.get(c,Math.round(b.y));)c--;for(var d=Math.round(b.x);a.get(d,Math.round(b.y));)d++;c=(c+d)/2;for(d=Math.round(b.y);a.get(Math.round(c),d);)d--;for(b=Math.round(b.y);a.get(Math.round(c),b);)b++;return{x:c,y:(d+b)/2}} +function sa(a){var b=[],c=[];let d=[];var e=[];for(let p=0;p<=a.height;p++){var f=0,g=!1;let t=[0,0,0,0,0];for(let v=-1;v<=a.width;v++){var h=a.get(v,p);if(h===g)f++;else{t=[t[1],t[2],t[3],t[4],f];f=1;g=h;var k=O(t)/7;k=Math.abs(t[0]-k)y>=w.bottom.startX&& +y<=w.bottom.endX||z>=w.bottom.startX&&y<=w.bottom.endX||y<=w.bottom.startX&&z>=w.bottom.endX&&1.5>t[2]/(w.bottom.endX-w.bottom.startX)&&.5y>=w.bottom.startX&&y<=w.bottom.endX||z>=w.bottom.startX&&y<=w.bottom.endX||y<=w.bottom.startX&&z>=w.bottom.endX&&1.5>t[2]/(w.bottom.endX-w.bottom.startX)&&.5v.bottom.y!==p&&2<=v.bottom.y-v.top.y));c=c.filter(v=>v.bottom.y===p);d.push(...e.filter(v=>v.bottom.y!==p));e=e.filter(v=>v.bottom.y===p)}b.push(...c.filter(p=>2<=p.bottom.y-p.top.y));d.push(...e);c=[];for(var l of b)2>l.bottom.y-l.top.y||(b=(l.top.startX+l.top.endX+l.bottom.startX+l.bottom.endX)/4,e=(l.top.y+l.bottom.y+1)/2,a.get(Math.round(b),Math.round(e))&&(f=[l.top.endX-l.top.startX,l.bottom.endX-l.bottom.startX,l.bottom.y-l.top.y+ +1],f=O(f)/f.length,g=S({x:Math.round(b),y:Math.round(e)},[1,1,3,1,1],a),c.push({score:g,x:b,y:e,size:f})));if(3>c.length)return null;c.sort((p,t)=>p.score-t.score);l=[];for(b=0;bp.score-t.score);l.push({points:[e,f[0],f[1]],score:e.score+f[0].score+f[1].score})}l.sort((p,t)=>p.score-t.score);let {topRight:q,topLeft:r,bottomLeft:u}=qa(...l[0].points); +l=U(a,d,q,r,u);n=[];l&&n.push({alignmentPattern:{x:l.alignmentPattern.x,y:l.alignmentPattern.y},bottomLeft:{x:u.x,y:u.y},dimension:l.dimension,topLeft:{x:r.x,y:r.y},topRight:{x:q.x,y:q.y}});l=T(a,q);b=T(a,r);c=T(a,u);(a=U(a,d,l,b,c))&&n.push({alignmentPattern:{x:a.alignmentPattern.x,y:a.alignmentPattern.y},bottomLeft:{x:c.x,y:c.y},topLeft:{x:b.x,y:b.y},topRight:{x:l.x,y:l.y},dimension:a.dimension});return 0===n.length?null:n} +function U(a,b,c,d,e){let f,g;try{({dimension:f,moduleSize:g}=ra(d,c,e,a))}catch(l){return null}var h=c.x-d.x+e.x,k=c.y-d.y+e.y;c=(N(d,e)+N(d,c))/2/g;e=1-3/c;let m={x:d.x+e*(h-d.x),y:d.y+e*(k-d.y)};b=b.map(l=>{const n=(l.top.startX+l.top.endX+l.bottom.startX+l.bottom.endX)/4;l=(l.top.y+l.bottom.y+1)/2;if(a.get(Math.floor(n),Math.floor(l))){var q=S({x:Math.floor(n),y:Math.floor(l)},[1,1,1],a)+N({x:n,y:l},m);return{x:n,y:l,score:q}}}).filter(l=>!!l).sort((l,n)=>l.score-n.score);return{alignmentPattern:15<= +c&&b.length?b[0]:m,dimension:f}} +function V(a){var b=sa(a);if(!b)return null;for(let e of b){b=pa(a,e);var c=b.matrix;if(null==c)c=null;else{var d=L(c);if(d)c=d;else{for(d=0;d{a[c]=b[c]})} +function X(a,b,c,d={}){let e=Object.create(null);W(e,ta);W(e,d);d="onlyInvert"===e.inversionAttempts||"invertFirst"===e.inversionAttempts;var f="attemptBoth"===e.inversionAttempts||d;var g=e.greyScaleWeights,h=e.canOverwriteImage,k=b*c;if(a.length!==4*k)throw Error("Malformed data passed to binarizer.");var m=0;if(h){var l=new Uint8ClampedArray(a.buffer,m,k);m+=k}l=new A(b,c,l);if(g.useIntegerApproximation)for(var n=0;n>8)}else for(n=0;nv;v++)for(let w=0;8>w;w++){let aa=l.get(8*r+w,8*q+v);p=Math.min(p,aa);t=Math.max(t,aa)}v=(p+t)/2;v=Math.min(255,1.11*v);24>=t-p&&(v=p/2,0a?2:a>c?c:a;h=n-3;h=2>b?2:b>h?h:b;k=0;for(m=-2;2>=m;m++)for(p=-2;2>=p;p++)k+=u.get(c+m,h+p);c=k/25;for(h=0;8>h;h++)for(k=0;8>k;k++)m=8*a+h,p=8*b+k,t=l.get(m,p),q.set(m,p,t<=c),f&&r.set(m,p,!(t<=c))}f=f?{binarized:q,inverted:r}:{binarized:q};let {binarized:z,inverted:y}=f;(f=V(d? +y:z))||"attemptBoth"!==e.inversionAttempts&&"invertFirst"!==e.inversionAttempts||(f=V(d?z:y));return f}X.default=X;let Y="dontInvert",Z={red:77,green:150,blue:29,useIntegerApproximation:!0}; +self.onmessage=a=>{let b=a.data.id,c=a.data.data;switch(a.data.type){case "decode":(a=X(c.data,c.width,c.height,{inversionAttempts:Y,greyScaleWeights:Z}))?self.postMessage({id:b,type:"qrResult",data:a.data,cornerPoints:[a.location.topLeftCorner,a.location.topRightCorner,a.location.bottomRightCorner,a.location.bottomLeftCorner]}):self.postMessage({id:b,type:"qrResult",data:null});break;case "grayscaleWeights":Z.red=c.red;Z.green=c.green;Z.blue=c.blue;Z.useIntegerApproximation=c.useIntegerApproximation; +break;case "inversionMode":switch(c){case "original":Y="dontInvert";break;case "invert":Y="onlyInvert";break;case "both":Y="attemptBoth";break;default:throw Error("Invalid inversion mode");}break;case "close":self.close()}} +`, + ]), + { type: "application/javascript" } + ) + ); //# sourceMappingURL=qr-scanner-worker.min.js.map diff --git a/hackathon_site/event/static/event/js/qr-scanner.umd.min.js b/hackathon_site/event/static/event/js/qr-scanner.umd.min.js new file mode 100644 index 000000000..0bcdd4a46 --- /dev/null +++ b/hackathon_site/event/static/event/js/qr-scanner.umd.min.js @@ -0,0 +1,683 @@ +"use strict"; +(function (e, a) { + "object" === typeof exports && "undefined" !== typeof module + ? (module.exports = a()) + : "function" === typeof define && define.amd + ? define(a) + : ((e = "undefined" !== typeof globalThis ? globalThis : e || self), + (e.QrScanner = a())); +})(this, function () { + class e { + constructor(a, b, c, d, f) { + this._legacyCanvasSize = e.DEFAULT_CANVAS_SIZE; + this._preferredCamera = "environment"; + this._maxScansPerSecond = 25; + this._lastScanTimestamp = -1; + this._destroyed = this._flashOn = this._paused = this._active = !1; + this.$video = a; + this.$canvas = document.createElement("canvas"); + c && "object" === typeof c + ? (this._onDecode = b) + : (c || d || f + ? console.warn( + "You're using a deprecated version of the QrScanner constructor which will be removed in the future" + ) + : console.warn( + "Note that the type of the scan result passed to onDecode will change in the future. To already switch to the new api today, you can pass returnDetailedScanResult: true." + ), + (this._legacyOnDecode = b)); + b = "object" === typeof c ? c : {}; + this._onDecodeError = + b.onDecodeError || ("function" === typeof c ? c : this._onDecodeError); + this._calculateScanRegion = + b.calculateScanRegion || + ("function" === typeof d ? d : this._calculateScanRegion); + this._preferredCamera = b.preferredCamera || f || this._preferredCamera; + this._legacyCanvasSize = + "number" === typeof c + ? c + : "number" === typeof d + ? d + : this._legacyCanvasSize; + this._maxScansPerSecond = b.maxScansPerSecond || this._maxScansPerSecond; + this._onPlay = this._onPlay.bind(this); + this._onLoadedMetaData = this._onLoadedMetaData.bind(this); + this._onVisibilityChange = this._onVisibilityChange.bind(this); + this._updateOverlay = this._updateOverlay.bind(this); + a.disablePictureInPicture = !0; + a.playsInline = !0; + a.muted = !0; + let h = !1; + a.hidden && ((a.hidden = !1), (h = !0)); + document.body.contains(a) || (document.body.appendChild(a), (h = !0)); + c = a.parentElement; + if (b.highlightScanRegion || b.highlightCodeOutline) { + d = !!b.overlay; + this.$overlay = b.overlay || document.createElement("div"); + f = this.$overlay.style; + f.position = "absolute"; + f.display = "none"; + f.pointerEvents = "none"; + this.$overlay.classList.add("scan-region-highlight"); + if (!d && b.highlightScanRegion) { + this.$overlay.innerHTML = + ''; + try { + this.$overlay.firstElementChild.animate( + { transform: ["scale(.98)", "scale(1.01)"] }, + { + duration: 400, + iterations: Infinity, + direction: "alternate", + easing: "ease-in-out", + } + ); + } catch (m) {} + c.insertBefore(this.$overlay, this.$video.nextSibling); + } + b.highlightCodeOutline && + (this.$overlay.insertAdjacentHTML( + "beforeend", + '' + ), + (this.$codeOutlineHighlight = this.$overlay.lastElementChild)); + } + this._scanRegion = this._calculateScanRegion(a); + requestAnimationFrame(() => { + let m = window.getComputedStyle(a); + "none" === m.display && + (a.style.setProperty("display", "block", "important"), (h = !0)); + "visible" !== m.visibility && + (a.style.setProperty("visibility", "visible", "important"), + (h = !0)); + h && + (console.warn( + "QrScanner has overwritten the video hiding style to avoid Safari stopping the playback." + ), + (a.style.opacity = "0"), + (a.style.width = "0"), + (a.style.height = "0"), + this.$overlay && + this.$overlay.parentElement && + this.$overlay.parentElement.removeChild(this.$overlay), + delete this.$overlay, + delete this.$codeOutlineHighlight); + this.$overlay && this._updateOverlay(); + }); + a.addEventListener("play", this._onPlay); + a.addEventListener("loadedmetadata", this._onLoadedMetaData); + document.addEventListener("visibilitychange", this._onVisibilityChange); + window.addEventListener("resize", this._updateOverlay); + this._qrEnginePromise = e.createQrEngine(); + } + static set WORKER_PATH(a) { + console.warn( + "Setting QrScanner.WORKER_PATH is not required and not supported anymore. Have a look at the README for new setup instructions." + ); + } + static async hasCamera() { + try { + return !!(await e.listCameras(!1)).length; + } catch (a) { + return !1; + } + } + static async listCameras(a = !1) { + if (!navigator.mediaDevices) return []; + let b = async () => + (await navigator.mediaDevices.enumerateDevices()).filter( + (d) => "videoinput" === d.kind + ), + c; + try { + a && + (await b()).every((d) => !d.label) && + (c = await navigator.mediaDevices.getUserMedia({ + audio: !1, + video: !0, + })); + } catch (d) {} + try { + return (await b()).map((d, f) => ({ + id: d.deviceId, + label: d.label || (0 === f ? "Default Camera" : `Camera ${f + 1}`), + })); + } finally { + c && + (console.warn( + "Call listCameras after successfully starting a QR scanner to avoid creating a temporary video stream" + ), + e._stopVideoStream(c)); + } + } + async hasFlash() { + let a; + try { + if (this.$video.srcObject) { + if (!(this.$video.srcObject instanceof MediaStream)) return !1; + a = this.$video.srcObject; + } else a = (await this._getCameraStream()).stream; + return "torch" in a.getVideoTracks()[0].getSettings(); + } catch (b) { + return !1; + } finally { + a && + a !== this.$video.srcObject && + (console.warn( + "Call hasFlash after successfully starting the scanner to avoid creating a temporary video stream" + ), + e._stopVideoStream(a)); + } + } + isFlashOn() { + return this._flashOn; + } + async toggleFlash() { + this._flashOn ? await this.turnFlashOff() : await this.turnFlashOn(); + } + async turnFlashOn() { + if ( + !this._flashOn && + !this._destroyed && + ((this._flashOn = !0), this._active && !this._paused) + ) + try { + if (!(await this.hasFlash())) throw "No flash available"; + await this.$video.srcObject + .getVideoTracks()[0] + .applyConstraints({ advanced: [{ torch: !0 }] }); + } catch (a) { + throw ((this._flashOn = !1), a); + } + } + async turnFlashOff() { + this._flashOn && ((this._flashOn = !1), await this._restartVideoStream()); + } + destroy() { + this.$video.removeEventListener("loadedmetadata", this._onLoadedMetaData); + this.$video.removeEventListener("play", this._onPlay); + document.removeEventListener("visibilitychange", this._onVisibilityChange); + window.removeEventListener("resize", this._updateOverlay); + this._destroyed = !0; + this._flashOn = !1; + this.stop(); + e._postWorkerMessage(this._qrEnginePromise, "close"); + } + async start() { + if (this._destroyed) + throw Error( + "The QR scanner can not be started as it had been destroyed." + ); + if (!this._active || this._paused) + if ( + ("https:" !== window.location.protocol && + console.warn( + "The camera stream is only accessible if the page is transferred via https." + ), + (this._active = !0), + !document.hidden) + ) + if (((this._paused = !1), this.$video.srcObject)) + await this.$video.play(); + else + try { + let { stream: a, facingMode: b } = + await this._getCameraStream(); + !this._active || this._paused + ? e._stopVideoStream(a) + : (this._setVideoMirror(b), + (this.$video.srcObject = a), + await this.$video.play(), + this._flashOn && + ((this._flashOn = !1), + this.turnFlashOn().catch(() => {}))); + } catch (a) { + if (!this._paused) throw ((this._active = !1), a); + } + } + stop() { + this.pause(); + this._active = !1; + } + async pause(a = !1) { + this._paused = !0; + if (!this._active) return !0; + this.$video.pause(); + this.$overlay && (this.$overlay.style.display = "none"); + let b = () => { + this.$video.srcObject instanceof MediaStream && + (e._stopVideoStream(this.$video.srcObject), + (this.$video.srcObject = null)); + }; + if (a) return b(), !0; + await new Promise((c) => setTimeout(c, 300)); + if (!this._paused) return !1; + b(); + return !0; + } + async setCamera(a) { + a !== this._preferredCamera && + ((this._preferredCamera = a), await this._restartVideoStream()); + } + static async scanImage(a, b, c, d, f = !1, h = !1) { + let m, + n = !1; + b && + ("scanRegion" in b || + "qrEngine" in b || + "canvas" in b || + "disallowCanvasResizing" in b || + "alsoTryWithoutScanRegion" in b || + "returnDetailedScanResult" in b) + ? ((m = b.scanRegion), + (c = b.qrEngine), + (d = b.canvas), + (f = b.disallowCanvasResizing || !1), + (h = b.alsoTryWithoutScanRegion || !1), + (n = !0)) + : b || c || d || f || h + ? console.warn( + "You're using a deprecated api for scanImage which will be removed in the future." + ) + : console.warn( + "Note that the return type of scanImage will change in the future. To already switch to the new api today, you can pass returnDetailedScanResult: true." + ); + b = !!c; + try { + let p, k; + [c, p] = await Promise.all([c || e.createQrEngine(), e._loadImage(a)]); + [d, k] = e._drawToCanvas(p, m, d, f); + let q; + if (c instanceof Worker) { + let g = c; + b || e._postWorkerMessageSync(g, "inversionMode", "both"); + q = await new Promise((l, v) => { + let w, + u, + r, + y = -1; + u = (t) => { + t.data.id === y && + (g.removeEventListener("message", u), + g.removeEventListener("error", r), + clearTimeout(w), + null !== t.data.data + ? l({ + data: t.data.data, + cornerPoints: e._convertPoints( + t.data.cornerPoints, + m + ), + }) + : v(e.NO_QR_CODE_FOUND)); + }; + r = (t) => { + g.removeEventListener("message", u); + g.removeEventListener("error", r); + clearTimeout(w); + v( + "Scanner error: " + + (t ? t.message || t : "Unknown Error") + ); + }; + g.addEventListener("message", u); + g.addEventListener("error", r); + w = setTimeout(() => r("timeout"), 1e4); + let x = k.getImageData(0, 0, d.width, d.height); + y = e._postWorkerMessageSync(g, "decode", x, [x.data.buffer]); + }); + } else + q = await Promise.race([ + new Promise((g, l) => + window.setTimeout(() => l("Scanner error: timeout"), 1e4) + ), + (async () => { + try { + var [g] = await c.detect(d); + if (!g) throw e.NO_QR_CODE_FOUND; + return { + data: g.rawValue, + cornerPoints: e._convertPoints(g.cornerPoints, m), + }; + } catch (l) { + g = l.message || l; + if (/not implemented|service unavailable/.test(g)) + return ( + (e._disableBarcodeDetector = !0), + e.scanImage(a, { + scanRegion: m, + canvas: d, + disallowCanvasResizing: f, + alsoTryWithoutScanRegion: h, + }) + ); + throw `Scanner error: ${g}`; + } + })(), + ]); + return n ? q : q.data; + } catch (p) { + if (!m || !h) throw p; + let k = await e.scanImage(a, { + qrEngine: c, + canvas: d, + disallowCanvasResizing: f, + }); + return n ? k : k.data; + } finally { + b || e._postWorkerMessage(c, "close"); + } + } + setGrayscaleWeights(a, b, c, d = !0) { + e._postWorkerMessage(this._qrEnginePromise, "grayscaleWeights", { + red: a, + green: b, + blue: c, + useIntegerApproximation: d, + }); + } + setInversionMode(a) { + e._postWorkerMessage(this._qrEnginePromise, "inversionMode", a); + } + static async createQrEngine(a) { + a && + console.warn( + "Specifying a worker path is not required and not supported anymore." + ); + a = () => + import("./qr-scanner-worker.min.js").then((c) => c.createWorker()); + if ( + !( + !e._disableBarcodeDetector && + "BarcodeDetector" in window && + BarcodeDetector.getSupportedFormats && + (await BarcodeDetector.getSupportedFormats()).includes("qr_code") + ) + ) + return a(); + let b = navigator.userAgentData; + return b && + b.brands.some(({ brand: c }) => /Chromium/i.test(c)) && + /mac ?OS/i.test(b.platform) && + (await b + .getHighEntropyValues(["architecture", "platformVersion"]) + .then( + ({ architecture: c, platformVersion: d }) => + /arm/i.test(c || "arm") && 13 <= parseInt(d || "13") + ) + .catch(() => !0)) + ? a() + : new BarcodeDetector({ formats: ["qr_code"] }); + } + _onPlay() { + this._scanRegion = this._calculateScanRegion(this.$video); + this._updateOverlay(); + this.$overlay && (this.$overlay.style.display = ""); + this._scanFrame(); + } + _onLoadedMetaData() { + this._scanRegion = this._calculateScanRegion(this.$video); + this._updateOverlay(); + } + _onVisibilityChange() { + document.hidden ? this.pause() : this._active && this.start(); + } + _calculateScanRegion(a) { + let b = Math.round((2 / 3) * Math.min(a.videoWidth, a.videoHeight)); + return { + x: Math.round((a.videoWidth - b) / 2), + y: Math.round((a.videoHeight - b) / 2), + width: b, + height: b, + downScaledWidth: this._legacyCanvasSize, + downScaledHeight: this._legacyCanvasSize, + }; + } + _updateOverlay() { + requestAnimationFrame(() => { + if (this.$overlay) { + var a = this.$video, + b = a.videoWidth, + c = a.videoHeight, + d = a.offsetWidth, + f = a.offsetHeight, + h = a.offsetLeft, + m = a.offsetTop, + n = window.getComputedStyle(a), + p = n.objectFit, + k = b / c, + q = d / f; + switch (p) { + case "none": + var g = b; + var l = c; + break; + case "fill": + g = d; + l = f; + break; + default: + ("cover" === p ? k > q : k < q) + ? ((l = f), (g = l * k)) + : ((g = d), (l = g / k)), + "scale-down" === p && + ((g = Math.min(g, b)), (l = Math.min(l, c))); + } + var [v, w] = n.objectPosition.split(" ").map((r, y) => { + const x = parseFloat(r); + return r.endsWith("%") ? ((y ? f - l : d - g) * x) / 100 : x; + }); + n = this._scanRegion.width || b; + q = this._scanRegion.height || c; + p = this._scanRegion.x || 0; + var u = this._scanRegion.y || 0; + k = this.$overlay.style; + k.width = `${(n / b) * g}px`; + k.height = `${(q / c) * l}px`; + k.top = `${m + w + (u / c) * l}px`; + c = /scaleX\(-1\)/.test(a.style.transform); + k.left = `${ + h + (c ? d - v - g : v) + ((c ? b - p - n : p) / b) * g + }px`; + k.transform = a.style.transform; + } + }); + } + static _convertPoints(a, b) { + if (!b) return a; + let c = b.x || 0, + d = b.y || 0, + f = b.width && b.downScaledWidth ? b.width / b.downScaledWidth : 1; + b = b.height && b.downScaledHeight ? b.height / b.downScaledHeight : 1; + for (let h of a) (h.x = h.x * f + c), (h.y = h.y * b + d); + return a; + } + _scanFrame() { + !this._active || + this.$video.paused || + this.$video.ended || + ("requestVideoFrameCallback" in this.$video + ? this.$video.requestVideoFrameCallback.bind(this.$video) + : requestAnimationFrame)(async () => { + if (!(1 >= this.$video.readyState)) { + var a = Date.now() - this._lastScanTimestamp, + b = 1e3 / this._maxScansPerSecond; + a < b && (await new Promise((d) => setTimeout(d, b - a))); + this._lastScanTimestamp = Date.now(); + try { + var c = await e.scanImage(this.$video, { + scanRegion: this._scanRegion, + qrEngine: this._qrEnginePromise, + canvas: this.$canvas, + }); + } catch (d) { + if (!this._active) return; + this._onDecodeError(d); + } + !e._disableBarcodeDetector || + (await this._qrEnginePromise) instanceof Worker || + (this._qrEnginePromise = e.createQrEngine()); + c + ? (this._onDecode + ? this._onDecode(c) + : this._legacyOnDecode && + this._legacyOnDecode(c.data), + this.$codeOutlineHighlight && + (clearTimeout( + this._codeOutlineHighlightRemovalTimeout + ), + (this._codeOutlineHighlightRemovalTimeout = void 0), + this.$codeOutlineHighlight.setAttribute( + "viewBox", + `${this._scanRegion.x || 0} ` + + `${this._scanRegion.y || 0} ` + + `${ + this._scanRegion.width || + this.$video.videoWidth + } ` + + `${ + this._scanRegion.height || + this.$video.videoHeight + }` + ), + this.$codeOutlineHighlight.firstElementChild.setAttribute( + "points", + c.cornerPoints + .map(({ x: d, y: f }) => `${d},${f}`) + .join(" ") + ), + (this.$codeOutlineHighlight.style.display = ""))) + : this.$codeOutlineHighlight && + !this._codeOutlineHighlightRemovalTimeout && + (this._codeOutlineHighlightRemovalTimeout = setTimeout( + () => + (this.$codeOutlineHighlight.style.display = + "none"), + 100 + )); + } + this._scanFrame(); + }); + } + _onDecodeError(a) { + a !== e.NO_QR_CODE_FOUND && console.log(a); + } + async _getCameraStream() { + if (!navigator.mediaDevices) throw "Camera not found."; + let a = /^(environment|user)$/.test(this._preferredCamera) + ? "facingMode" + : "deviceId", + b = [{ width: { min: 1024 } }, { width: { min: 768 } }, {}], + c = b.map((d) => + Object.assign({}, d, { [a]: { exact: this._preferredCamera } }) + ); + for (let d of [...c, ...b]) + try { + let f = await navigator.mediaDevices.getUserMedia({ + video: d, + audio: !1, + }), + h = + this._getFacingMode(f) || + (d.facingMode + ? this._preferredCamera + : "environment" === this._preferredCamera + ? "user" + : "environment"); + return { stream: f, facingMode: h }; + } catch (f) {} + throw "Camera not found."; + } + async _restartVideoStream() { + let a = this._paused; + (await this.pause(!0)) && !a && this._active && (await this.start()); + } + static _stopVideoStream(a) { + for (let b of a.getTracks()) b.stop(), a.removeTrack(b); + } + _setVideoMirror(a) { + this.$video.style.transform = "scaleX(" + ("user" === a ? -1 : 1) + ")"; + } + _getFacingMode(a) { + return (a = a.getVideoTracks()[0]) + ? /rear|back|environment/i.test(a.label) + ? "environment" + : /front|user|face/i.test(a.label) + ? "user" + : null + : null; + } + static _drawToCanvas(a, b, c, d = !1) { + c = c || document.createElement("canvas"); + let f = b && b.x ? b.x : 0, + h = b && b.y ? b.y : 0, + m = b && b.width ? b.width : a.videoWidth || a.width, + n = b && b.height ? b.height : a.videoHeight || a.height; + d || + ((d = b && b.downScaledWidth ? b.downScaledWidth : m), + (b = b && b.downScaledHeight ? b.downScaledHeight : n), + c.width !== d && (c.width = d), + c.height !== b && (c.height = b)); + b = c.getContext("2d", { alpha: !1 }); + b.imageSmoothingEnabled = !1; + b.drawImage(a, f, h, m, n, 0, 0, c.width, c.height); + return [c, b]; + } + static async _loadImage(a) { + if (a instanceof Image) return await e._awaitImageLoad(a), a; + if ( + a instanceof HTMLVideoElement || + a instanceof HTMLCanvasElement || + a instanceof SVGImageElement || + ("OffscreenCanvas" in window && a instanceof OffscreenCanvas) || + ("ImageBitmap" in window && a instanceof ImageBitmap) + ) + return a; + if ( + a instanceof File || + a instanceof Blob || + a instanceof URL || + "string" === typeof a + ) { + let b = new Image(); + b.src = + a instanceof File || a instanceof Blob + ? URL.createObjectURL(a) + : a.toString(); + try { + return await e._awaitImageLoad(b), b; + } finally { + (a instanceof File || a instanceof Blob) && + URL.revokeObjectURL(b.src); + } + } else throw "Unsupported image type."; + } + static async _awaitImageLoad(a) { + (a.complete && 0 !== a.naturalWidth) || + (await new Promise((b, c) => { + let d = (f) => { + a.removeEventListener("load", d); + a.removeEventListener("error", d); + f instanceof ErrorEvent ? c("Image load error") : b(); + }; + a.addEventListener("load", d); + a.addEventListener("error", d); + })); + } + static async _postWorkerMessage(a, b, c, d) { + return e._postWorkerMessageSync(await a, b, c, d); + } + static _postWorkerMessageSync(a, b, c, d) { + if (!(a instanceof Worker)) return -1; + let f = e._workerMessageId++; + a.postMessage({ id: f, type: b, data: c }, d); + return f; + } + } + e.DEFAULT_CANVAS_SIZE = 400; + e.NO_QR_CODE_FOUND = "No QR code found"; + e._disableBarcodeDetector = !1; + e._workerMessageId = 0; + return e; +}); +//# sourceMappingURL=qr-scanner.umd.min.js.map diff --git a/hackathon_site/event/static/event/styles/scss/_variables.scss b/hackathon_site/event/static/event/styles/scss/_variables.scss index f56666829..a6f8b27e9 100644 --- a/hackathon_site/event/static/event/styles/scss/_variables.scss +++ b/hackathon_site/event/static/event/styles/scss/_variables.scss @@ -7,7 +7,13 @@ $colors: ( dark: #002f5e, backgroundColor: #ddeeff, error: #b00020, + errorBackground: #eed6da, success: #0b890b, + successBackground: #e4f1e4, + info: #044f9b, + infoBackground: #cfe1f1, + warning: #af5e00, + warningBackground: #e7d2b9, primaryOnHover: #08529c, secondaryOnHover: #ce8100, errorOnHover: #870000, diff --git a/hackathon_site/event/static/event/styles/scss/styles.scss b/hackathon_site/event/static/event/styles/scss/styles.scss index ce1079ac0..92da72e7c 100644 --- a/hackathon_site/event/static/event/styles/scss/styles.scss +++ b/hackathon_site/event/static/event/styles/scss/styles.scss @@ -85,7 +85,7 @@ strong { } .colorBtn { - font-weight: bold !important; + font-weight: 600 !important; text-align: center; text-transform: uppercase; color: color(font) !important; @@ -523,4 +523,38 @@ strong { margin: 5px 0 20px; } } + +.banner { + width: 100%; + padding: 10px; + text-align: center; + border-radius: 5px; + border: 2px; + margin: 10px 0; + + &success { + border: 1px solid color(success); + background-color: color(successBackground); + color: color(success); + } + + &error { + border: 1px solid color(error); + background-color: color(errorBackground); + color: color(error); + } + + &info { + border: 1px solid color(info); + background-color: color(infoBackground); + color: color(info); + } + + &warning { + border: 1px solid color(warning); + background-color: color(warningBackground); + color: color(warning); + } +} + // End of dashboard stuff diff --git a/hackathon_site/event/urls.py b/hackathon_site/event/urls.py index 2482c8fd8..9b1f7ed8d 100644 --- a/hackathon_site/event/urls.py +++ b/hackathon_site/event/urls.py @@ -1,6 +1,6 @@ from django.contrib.auth import views as auth_views from django.urls import path, reverse_lazy -from event.views import IndexView, DashboardView +from event.views import IndexView, DashboardView, QRScannerView from event.forms import ( PasswordChangeForm, PasswordResetForm, @@ -21,6 +21,7 @@ ), path("accounts/logout/", auth_views.LogoutView.as_view(), name="logout",), path("dashboard/", DashboardView.as_view(), name="dashboard"), + path("dashboard/qrscan/", QRScannerView.as_view(), name="qr-scanner"), path( "accounts/change_password/", auth_views.PasswordChangeView.as_view( diff --git a/hackathon_site/event/views.py b/hackathon_site/event/views.py index 11e86fe70..f3fdb6be3 100644 --- a/hackathon_site/event/views.py +++ b/hackathon_site/event/views.py @@ -1,5 +1,6 @@ from datetime import datetime, timedelta +from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin from django.db import transaction from django.shortcuts import redirect @@ -15,12 +16,16 @@ from rest_framework.filters import SearchFilter -from hackathon_site.utils import is_registration_open -from registration.forms import JoinTeamForm -from registration.models import Team as RegistrationTeam +from hackathon_site.utils import ( + is_registration_open, + is_hackathon_happening, + NoEventOccurringException, + get_curr_sign_in_time, +) +from registration.forms import JoinTeamForm, SignInForm +from registration.models import Team as RegistrationTeam, User, Application - -from event.models import Team as EventTeam +from event.models import Team as EventTeam, UserActivity from event.serializers import TeamSerializer from event.api_filters import TeamFilter from event.permissions import FullDjangoModelPermissions @@ -47,10 +52,14 @@ def get_context_data(self, **kwargs): class DashboardView(LoginRequiredMixin, FormView): - template_name = "event/dashboard_base.html" # Form submits should take the user back to the dashboard success_url = reverse_lazy("event:dashboard") + def get_template_names(self): + if self.request.user.is_staff: + return "event/dashboard_admin.html" + return "event/dashboard_base.html" + def get_form(self, form_class=None): """ The dashboard can have different forms, but not at the same time: @@ -188,6 +197,66 @@ def post(self, request, *args, **kwargs): return super().post(request, *args, **kwargs) +class QRScannerView(LoginRequiredMixin, FormView): + success_url = reverse_lazy("event:qr-scanner") + + def get_template_names(self): + if self.request.user.is_staff: + return "event/admin_qr_scanner.html" + return Exception("You do not have permission to view this page.") + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + + if isinstance(context["form"], SignInForm): + context["sign_in_form"] = context["form"] + + return context + + def get_form(self, form_class=None): + if form_class is not None: + return form_class(**self.get_form_kwargs()) + + if is_hackathon_happening(): + return SignInForm(**self.get_form_kwargs()) + + return None + + def form_valid(self, form): + if isinstance(form, SignInForm): + try: + user = User.objects.get(email__exact=form.cleaned_data["email"]) + sign_in_event = get_curr_sign_in_time() + now = datetime.now().replace(tzinfo=settings.TZ_INFO) + + try: + user_activity = UserActivity.objects.get(user__exact=user) + setattr(user_activity, sign_in_event, now) + user_activity.save() + except UserActivity.DoesNotExist: + sign_in_obj = {} + sign_in_obj[sign_in_event] = now + UserActivity.objects.create(user=user, **sign_in_obj) + + messages.success( + self.request, + f'User {form.cleaned_data["email"]} successfully signed in', + ) + except NoEventOccurringException as e: + messages.info(self.request, str(e)) + except Exception as e: + messages.error( + self.request, + f'User {form.cleaned_data["email"]} could not sign in due to: {str(e)}', + ) + + return redirect(self.get_success_url()) + + @transaction.atomic + def post(self, request, *args, **kwargs): + return super().post(request, *args, **kwargs) + + class TeamListView(mixins.ListModelMixin, generics.GenericAPIView): queryset = EventTeam.objects.all() serializer_class = TeamSerializer diff --git a/hackathon_site/hackathon_site/jinja2.py b/hackathon_site/hackathon_site/jinja2.py index 43fc19c27..4ab224380 100644 --- a/hackathon_site/hackathon_site/jinja2.py +++ b/hackathon_site/hackathon_site/jinja2.py @@ -1,10 +1,16 @@ +from django.contrib import messages from django.contrib.staticfiles.storage import staticfiles_storage from django.conf import settings from django.urls import reverse from django.utils.timezone import template_localtime from jinja2 import Environment -from hackathon_site.utils import is_registration_open +from hackathon_site.utils import ( + is_registration_open, + get_sign_in_interval, + get_curr_sign_in_time, +) + # In testing, nothing in this file can be overwritten using the # @patch or @override_settings decorators, because it is evaluated before @@ -21,6 +27,9 @@ def environment(**options): "url": reverse, "localtime": template_localtime, "is_registration_open": is_registration_open, + "get_messages": messages.get_messages, + "get_sign_in_interval": get_sign_in_interval, + "get_curr_sign_in_time": get_curr_sign_in_time, # Variables "hackathon_name": settings.HACKATHON_NAME, "hss_url": settings.HSS_URL, @@ -37,6 +46,7 @@ def environment(**options): "chat_room_link": settings.CHAT_ROOM[1], "using_teams": settings.TEAMS, "using_rsvp": settings.RSVP, + "sign_in_times": settings.SIGN_IN_TIMES, } ) return env diff --git a/hackathon_site/hackathon_site/settings/__init__.py b/hackathon_site/hackathon_site/settings/__init__.py index ebfb97519..714005f16 100644 --- a/hackathon_site/hackathon_site/settings/__init__.py +++ b/hackathon_site/hackathon_site/settings/__init__.py @@ -92,6 +92,7 @@ "django_filters", "client_side_image_cropping", "captcha", + "qrcode", "dashboard", "registration", "event", @@ -146,6 +147,7 @@ LOGIN_REDIRECT_URL = reverse_lazy("event:dashboard") LOGOUT_REDIRECT_URL = reverse_lazy("event:index") +MESSAGE_STORAGE = "django.contrib.messages.storage.session.SessionStorage" # Database # https://docs.djangoproject.com/en/3.0/ref/settings/#databases @@ -301,6 +303,36 @@ HARDWARE_SIGN_OUT_START_DATE = datetime(2020, 9, 1, tzinfo=TZ_INFO) HARDWARE_SIGN_OUT_END_DATE = datetime(2023, 9, 30, tzinfo=TZ_INFO) +# sign in times must be between EVENT_START_DATE and EVENT_END_DATE and in chronological order +# the number of sign in times MUST MATCH the number of columns in UserActivityTable +SIGN_IN_TIMES = [ + { + "name": "sign_in", + "description": "Hackathon Sign In", + "time": datetime(2023, 1, 13, 17, 0, 0, tzinfo=TZ_INFO), # Oct 10th @ 11am + }, + { + "name": "lunch1", + "description": "Lunch Day 1", + "time": datetime(2023, 1, 13, 22, 0, 0, tzinfo=TZ_INFO), # Oct 10th @ 2pm + }, + { + "name": "dinner1", + "description": "Dinner Day 1", + "time": datetime(2023, 1, 14, 6, 0, 0, tzinfo=TZ_INFO), # Oct 10th @ 6pm + }, + { + "name": "breakfast2", + "description": "Breakfast Day 2", + "time": datetime(2023, 1, 14, 11, 0, 0, tzinfo=TZ_INFO), # Oct 11th @ 9am + }, + { + "name": "lunch2", + "description": "Lunch Day 2", + "time": datetime(2023, 1, 14, 17, 0, 0, tzinfo=TZ_INFO), # Oct 11th @ 12pm + }, +] + # Registration user requirements MINIMUM_AGE = 14 diff --git a/hackathon_site/hackathon_site/utils.py b/hackathon_site/hackathon_site/utils.py index 209f65bab..5c8d54984 100644 --- a/hackathon_site/hackathon_site/utils.py +++ b/hackathon_site/hackathon_site/utils.py @@ -1,8 +1,16 @@ from datetime import datetime +from dateutil.relativedelta import relativedelta from django.conf import settings +class NoEventOccurringException(Exception): + def __init__(self): + super().__init__( + "There is currently no event happening for the user to sign in." + ) + + def is_registration_open(): """ Determine whether registration is currently open @@ -15,3 +23,35 @@ def is_registration_open(): # is configured to match TIME_ZONE. We then make the datetime object timezone-aware. now = datetime.now().replace(tzinfo=settings.TZ_INFO) return settings.REGISTRATION_OPEN_DATE <= now < settings.REGISTRATION_CLOSE_DATE + + +def is_hackathon_happening(): + if settings.IN_TESTING or settings.DEBUG: + return True + + now = datetime.now().replace(tzinfo=settings.TZ_INFO) + return settings.EVENT_START_DATE <= now < settings.EVENT_END_DATE + + +def get_curr_sign_in_time(useDescription=False): + now = datetime.now().replace(tzinfo=settings.TZ_INFO) + for event in settings.SIGN_IN_TIMES: + start_interval = event["time"] - relativedelta(hours=1) + end_interval = event["time"] + relativedelta(hours=1) + if start_interval <= now <= end_interval: + return event["description"] if useDescription else event["name"] + + if useDescription: + return None + raise NoEventOccurringException() + + +# assumes interval won't overlap between different months or years +def get_sign_in_interval(time): + start_interval = time - relativedelta(hours=1) + end_interval = time + relativedelta(hours=1) + + if start_interval.day == end_interval.day: + return f"{start_interval.strftime('%H:%M')} - {end_interval.strftime('%H:%M')}, {start_interval.strftime('%b %d')}" + + return f"{start_interval.strftime('%H:%M, %b %d')} - {end_interval.strftime('%H:%M, %b %d')}" diff --git a/hackathon_site/registration/forms.py b/hackathon_site/registration/forms.py index be9abf98c..87c2fe3c7 100644 --- a/hackathon_site/registration/forms.py +++ b/hackathon_site/registration/forms.py @@ -8,9 +8,10 @@ from django_registration import validators from django.conf import settings -from hackathon_site.utils import is_registration_open +from hackathon_site.utils import is_registration_open, is_hackathon_happening from registration.models import Application, Team, User from registration.widgets import MaterialFileInput +from review.models import Review class SignUpForm(UserCreationForm): @@ -211,3 +212,50 @@ def clean_team_code(self): raise forms.ValidationError(_(f"Team {team_code} is full.")) return team_code + + +class SignInForm(forms.Form): + email = forms.EmailField() + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.label_suffix = "" + self.error_css_class = "invalid" + + def clean(self): + if not is_hackathon_happening(): + raise forms.ValidationError( + _("You cannot sign in outside of the hackathon period."), + code="invalid_sign_in_time", + ) + + return super().clean() + + def clean_email(self): + email = self.cleaned_data["email"] + + try: + user = User.objects.get(email__exact=email) + application = Application.objects.get(user__exact=user) + review = Review.objects.get(application__exact=application) + if review.status == "Accepted": + if settings.RSVP and application.rsvp is None: + raise forms.ValidationError( + _(f"User {email} has not RSVP'd to the hackathon") + ) + else: + raise forms.ValidationError( + _( + f"User {email} has not been Accepted to attend {settings.HACKATHON_NAME}" + ) + ) + except User.DoesNotExist: + raise forms.ValidationError(_(f"User {email} does not exist.")) + except Application.DoesNotExist: + raise forms.ValidationError( + _(f"User {email} has not applied to {settings.HACKATHON_NAME}") + ) + except Exception as e: + raise e + + return email diff --git a/hackathon_site/requirements.txt b/hackathon_site/requirements.txt index d499015a4..f3ad7ebdd 100644 --- a/hackathon_site/requirements.txt +++ b/hackathon_site/requirements.txt @@ -32,6 +32,7 @@ python-dateutil==2.8.2 pyparsing==2.4.7 pytz==2020.1 PyYAML==5.4 +qrcode==7.3.1 redis==3.5.3 regex==2021.4.4 requests==2.25.1