From cbd3e19d7a2d75c3451147fa7d635c6b9106cef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Enrique=20G=C3=B3mez=20G=C3=B3mez?= Date: Mon, 10 Oct 2022 20:59:37 +0000 Subject: [PATCH] main[ADD]attendance_flask:Codigos QR para asistencias --- get_query.py | 28 ++++++++ main.py | 136 +++++++++++++++++++++++++---------- schema.sql | 10 ++- static/scan.html | 33 --------- templates/template.html | 155 ++++++++++++++++++++++++++++++---------- templates/thanks.html | 120 ++++++++++++++++++++++++------- 6 files changed, 343 insertions(+), 139 deletions(-) create mode 100644 get_query.py delete mode 100644 static/scan.html diff --git a/get_query.py b/get_query.py new file mode 100644 index 0000000..2557f0c --- /dev/null +++ b/get_query.py @@ -0,0 +1,28 @@ +import sqlite3 +from datetime import datetime +import pytz +import requests + + +def get_db_connection(): + conn = sqlite3.connect('database.db') + conn.row_factory = sqlite3.Row + return conn + +conn = get_db_connection() +data = conn.execute('SELECT user, datein, dateout, ip_branch, id FROM hits WHERE dateout != ? AND dateinspe = ? ', ('', '')).fetchall() + +for d in data: + params ={ + 'employee_id': d[0], + 'check_in': d[1], + 'check_out': d[2], + 'ip_branch': d[3], + } + response = requests.post('http://137.184.126.24:8080/hr_attendance_extended/public/attendance/', json = params).json() + if response["message"]: + time = datetime.now(pytz.timezone('America/Bogota')).strftime("%Y-%m-%d %H:%M:%S") + conn.execute("UPDATE hits SET dateinspe = ? WHERE id = ?", (time, d[4])) + conn.commit() + else: + continue diff --git a/main.py b/main.py index da36e7b..ee731e6 100755 --- a/main.py +++ b/main.py @@ -2,106 +2,164 @@ import uuid import sqlite3 +import base64 from datetime import datetime -from flask import Flask, request, render_template, current_app +import pytz +import os +from flask import Flask, request, render_template from flask_uuid import FlaskUUID from flask_qrcode import QRcode from flask_socketio import SocketIO, emit from werkzeug.middleware.proxy_fix import ProxyFix import hashlib -import json - -# WIP: use sockets to broadcast when a hit to the expected uuid has been -# received, so that connected clients (the Chromium public browser) can -# refresh the qr code and update the list of users -# https://flask-socketio.readthedocs.io +import requests app = Flask(__name__) FlaskUUID(app) -#QRcode(app) qrcode = QRcode(app) app.config['SECRET_KEY'] = '53a8373e7ae652cd38beba15454b1dc4' -#app = ProxyFix(app, x_for=1, x_host=1) app.wsgi_app = ProxyFix(app.wsgi_app) socketio = SocketIO(app, async_mode=None) + def get_db_connection(): conn = sqlite3.connect('database.db') conn.row_factory = sqlite3.Row return conn + def hash_string(string): salt = 'xpwQDFQ7gvcQ--rgO7l9yA' return hashlib.md5(salt.encode() + string.encode()).hexdigest() + def check_hash(string, hashed_string): salt = 'xpwQDFQ7gvcQ--rgO7l9yA' return hashed_string == hashlib.md5(salt.encode() + string.encode()).hexdigest() -def generate_next_url(): + +def type_string(string): + return hashlib.md5(string.encode()).hexdigest() + + +def check_type(string): + if string == '02181ec55d150f6230547dbbf313e4f8': + type_check = 'CheckIn' + elif string == '231d5cc583e69d5b6c5bdced40dcc27c': + type_check = 'CheckOut' + else: + type_check = 'mistake' + return type_check + + +def generate_next_url(type, ip_branch): next_uuid = str(uuid.uuid1()) - url = request.url_root + str(next_uuid) + '/' + hash_string(next_uuid) + url = str(next_uuid) + '/' + hash_string(next_uuid) + \ + '/' + type_string(type) + '/' + (ip_branch) + message_bytes = url.encode('ascii') + base64_bytes = base64.b64encode(message_bytes) + url = base64_bytes.decode('ascii') return url + @app.route('/') def show_qr_and_list(): # TODO: reject direct connections to server; allow access only via proxy + get_list = requests.get('http://137.184.126.24:8080/hr_attendance_extended/public/attendance/').json() + list_ips = get_list["list_ips"] + ip_branch = request.environ.get('HTTP_X_REAL_IP', request.remote_addr) + if ip_branch in list_ips: + access = True + else: + access = False + return render_template("template.html", ip_branch=ip_branch, access=access) - return render_template("template.html") -@app.route('/scan') -def show_qr_reader(): - return current_app.send_static_file('scan.html') - -@app.route('//') -def catch_uuids(id, hashed): +@app.route('////') +def catch_uuids(id, hashed, type, ip_branch): user = request.headers.get('Remote-User') # TODO: Check directly with Authelia using https://auth.agofer.net/api/verify - time = datetime.now().strftime("%A %Y-%m-%d %H:%M:%S") + time = datetime.now(pytz.timezone('America/Bogota') + ).strftime("%Y-%m-%d %H:%M:%S") + type_check = check_type(str(type)) error = None data = [] conn = get_db_connection() - existing = conn.execute( - 'SELECT * FROM hits WHERE uuid = ?', (str(id),)).fetchone() + existing1 = conn.execute( + 'SELECT * FROM hits WHERE uuid1 = ?', (str(id),)).fetchone() + existing2 = conn.execute( + 'SELECT * FROM hits WHERE uuid2 = ?', (str(id),)).fetchone() if not user: error = 'NO_USERNAME' - elif existing: + elif existing1: + error = 'ALREADY_USED' + elif existing2: error = 'ALREADY_USED' elif not check_hash(str(id), str(hashed)): error = 'DIFFERENT_NODE' else: - conn.execute("INSERT INTO hits (uuid, user) VALUES (?, ?)", (str(id), user)) - conn.commit() - #socketio.emit('qr_used', {'data': (time, user, str(id))}) - url = generate_next_url() - qr = qrcode(url) - socketio.emit('qr_used', {'data': (qr, url, user, time)}) + if type_check == 'mistake': + error = 'DIFFERENT_TYPE' + else: + exit = conn.execute( + 'SELECT * FROM hits WHERE user = ? AND dateout = ?', (user, '')).fetchone() + if type_check == 'CheckIn': + if exit: + error = 'MISSING_EXIT' + else: + conn.execute( + "INSERT INTO hits (user, uuid1, datein) VALUES (?, ?, ?)", (user, str(id), time)) + conn.commit() + url = generate_next_url(type_check, str(ip_branch)) + qr = qrcode(url) + socketio.emit('qr_used', {'data': ( + qr, url, user, time, type_check,)}) + elif type_check == 'CheckOut': + if exit: + conn.execute( + "UPDATE hits SET dateout = ?, uuid2 = ?, ip_branch = ? WHERE user = ? AND dateout = ?", (time, str(id), str(ip_branch), user, '')) + conn.commit() + url = generate_next_url(type_check, str(ip_branch)) + qr = qrcode(url) + os.system('python get_query.py') + socketio.emit('qr_used', {'data': ( + qr, url, user, time, type_check)}) + else: + error = 'MISSING_ENTER' + data = conn.execute( + 'SELECT * FROM hits WHERE user = ? ORDER BY id DESC LIMIT 10', (user,)).fetchall() + conn.close() + return render_template('thanks.html', user=user, time=time, error=error, type=type_check, hits=data) - data = conn.execute( - 'SELECT * FROM hits WHERE user = ? ORDER BY id DESC LIMIT 10', (user,) - ).fetchall() - conn.close() - - return render_template('thanks.html', user=user, time=time, error=error, hits=data) @socketio.on('message') def handle_message(data): print('received message: ' + data) + @socketio.on('connection') def handle_initial_connection(json_data): - url = generate_next_url() + ip_branch = request.environ.get('HTTP_X_REAL_IP', request.remote_addr) + url = generate_next_url('CheckIn', ip_branch) + url2 = generate_next_url('CheckOut', ip_branch) qr = qrcode(url) + qr2 = qrcode(url2) conn = get_db_connection() - last_entries = conn.execute('SELECT user, created FROM hits ORDER BY id DESC LIMIT 10') + last_entries = conn.execute( + 'SELECT user, datein FROM hits WHERE datein != ? ORDER BY id DESC LIMIT 10', ('',)).fetchall() + last_entries2 = conn.execute( + 'SELECT user, dateout FROM hits WHERE dateout != ? ORDER BY id DESC LIMIT 10', ('',)).fetchall() array_entries = [] + array_entries2 = [] for entry in last_entries: array_entries.append((entry[0], entry[1])) + for entry2 in last_entries2: + array_entries2.append((entry2[0], entry2[1])) conn.close() - emit('initial_qr', {'data': (qr, url, array_entries)}) - #socketio.emit('initial_qr', {'data': (qr,)}) + emit('initial_qr', { + 'data': (qr, url, array_entries, qr2, url2, array_entries2)}) print('received json: ' + str(json_data)) + if __name__ == '__main__': - #app.run(host='0.0.0.0') socketio.run(app, host='0.0.0.0') diff --git a/schema.sql b/schema.sql index 961f533..5171122 100644 --- a/schema.sql +++ b/schema.sql @@ -2,8 +2,12 @@ DROP TABLE IF EXISTS hits; CREATE TABLE hits ( id INTEGER PRIMARY KEY AUTOINCREMENT, - created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - uuid TEXT NOT NULL, - user TEXT NOT NULL + user TEXT NOT NULL, + uuid1 TEXT NOT NULL, + uuid2 TEXT NOT NULL DEFAULT '', + datein TEXT NOT NULL DEFAULT '' , + dateout TEXT NOT NULL DEFAULT '', + ip_branch TEXT NOT NULL DEFAULT '', + dateinspe TEXT NOT NULL DEFAULT '' ); diff --git a/static/scan.html b/static/scan.html deleted file mode 100644 index a35bd92..0000000 --- a/static/scan.html +++ /dev/null @@ -1,33 +0,0 @@ - - - - Instascan - - - - - - - - - diff --git a/templates/template.html b/templates/template.html index 30a968e..345ea7d 100644 --- a/templates/template.html +++ b/templates/template.html @@ -1,60 +1,139 @@ + + - + -Example - - -

QR Code:

- {# -

For URL {{ request.url_root + next_uuid }}

- - #} -

For URL __loading...__

- -

Last 10 users:

-
    - {# -
      - {% for hit in hits %} -
    • {{ hit['created'] }}, {{ hit['user'] }}
    • - {% endfor %} -
    - #} - - + Códigos QR + + + + + {% if access == true %} +

    Códigos QR

    +

    IP: {{ ip_branch }}

    +
    +
    +

    Entrada

    + +

    Últimos 10 usuarios:

    +
    +
      +
      +
      +
      +

      Salida

      + +

      Últimos 10 usuarios:

      +
      +
        +
        +
        +
        + {% else %} +
        + + +
        + {% endif %} + + + \ No newline at end of file diff --git a/templates/thanks.html b/templates/thanks.html index 8c9fc16..70a321d 100644 --- a/templates/thanks.html +++ b/templates/thanks.html @@ -1,29 +1,97 @@ - - Example - - - {% if error == 'NO_USERNAME' %} -

        Error

        -

        No username received. Login here.

        - {% elif error == 'ALREADY_USED' %} -

        Error

        -

        Code has been used already.

        - {% elif error == 'DIFFERENT_NODE' %} -

        Error

        -

        Code was not generated by this system.

        - {% else %} -

        Thanks

        -

        At {{ time }},

        -

        Registered entrance or exit for user {{ user }} .

        -

        Last 10 registers for {{ user }}:

        -
          - {% for hit in hits %} -
        • {{ hit['created'] }}
        • - {% endfor %} -
        - {% endif %} - - + + + Registro + + + + +
        +

        + {% if error == 'NO_USERNAME' %} +

        Error

        +

        No se encontro el usuario.

        Inicia sesión AQUI.

        + {% elif error == 'ALREADY_USED' %} +

        Error

        +

        El enlace ya ha sido utilizado.

        inténteta de nuevo + AQUI.

        + {% elif error == 'DIFFERENT_NODE' %} +

        Error

        +

        El código no fue generado por este sistema.

        inténteta + de nuevo AQUI.

        + {% elif error == 'DIFFERENT_TYPE' %} +

        Error

        +

        No se encontró el tipo de entrada.

        inténteta de nuevo + AQUI.

        + {% elif error == 'MISSING_EXIT' %} +

        !Olvidaste la Salida!

        +

        Para registrar la entrada es necesario terminar la jornada de trabajo anterior.

        Escanear Salida AQUI.

        + {% elif error == 'MISSING_ENTER' %} +

        !Olvidaste la Entrada!

        +

        Para registrar la salida es necesario iniciar la jornada laboral.

        Escanear entrada AQUI.

        + {% else %} + + + + + Registro Guardado +
        +

        {{ time }}

        +

        Registrado {{ type }} para el usuario {{ user }} .

        +
        + Últimos 10 registros para {{ user }}: +
        +
          + {% if type == 'CheckOut' %} + {% for hit in hits %} +
        • {{ hit['dateout'] }}
        • + {% endfor %} + {% else %} + {% for hit in hits %} +
        • {{ hit['datein'] }}
        • + {% endfor %} + {% endif %} +
        + {% endif %} +

        +
        + + + + \ No newline at end of file