Esempio n. 1
0
def new_device_post(request, device_name, device_type):
    if 'file' not in request.files:
        flash('Neįkeltas failas', 'danger')
        return
    file = request.files['file']

    if file.filename == '':
        flash('Neįkeltas failas', 'danger')
        return

    if file and allowed_file(file.filename):
        filename = secure_filename(file.filename)

        try:
            response = file.read()

            image = cv2.imdecode(np.fromstring(response, np.uint8),
                                 cv2.IMREAD_COLOR)
            img = cv2.resize(image, (360, 480))
            code = decode(img)
            if code:
                add_new_device(code, device_name, device_type)
            else:
                flash("QR kodas nenuskaitytas!", "danger")
                return

        except Exception as Ex:
            flash("Prietaiso nepavyko užregistruoti!", "danger")
            Logger.log_error(Ex.args)

    else:
        flash("Failas nėra tinkamo formato!", "danger")
Esempio n. 2
0
def get_user_devices():
    try:
        session = create_user_session()

        if not table_exists("user_devices"):
            models.UserDevices.__table__.create(session.bind)
            session.commit()

        devices = session.query(models.UserDevices).all()

        session.close()

        devicesArr = []
        for device in devices:
            deviceObj = UserDevicesView(
                id=device.id,
                device_name=device.device_name,
                mac=device.mac,
                state=enums.DeviceState(device.status).name,
                date_added=str(device.date_added_local),
                device_type=device.device_type,
                device_type_name=enums.DeviceType(device.device_type).name,
                publish_interval=device.publish_interval,
                aes_key_interval=device.aes_key_interval)
            devicesArr.append(deviceObj)

        return jsonify({'data': [result.serialize for result in devicesArr]})
    except Exception as ex:
        Logger.log_error(ex.args[0])
        return messenger.raise_notification(
            False, 'Įvyko vidinė klaida:' + ex.args[0])
    finally:
        session.close()
Esempio n. 3
0
def delete_device_config(device, config_uuid):
    try:
        session = create_user_session()

        device_type = select_device_type(device)
        device_config = device_type.get_device_config_uuid(
            session, config_uuid)

        config_name = device_config.name
        if (device_config.job_state != enums.ConfigJobState.Running.value):
            configure_device_jobs(device, device_config, False)
            session.delete(device_config)
            session.commit()
            return messenger.raise_notification(
                True, 'Konfigūracija "' + config_name + '" panaikinta!')
        else:
            session.rollback()
            return messenger.raise_notification(
                False,
                'Rutininis darbas vykdomas pagal šią konfigūraciją! Atšaukite rutininį darbą!'
            )

    except Exception as ex:
        session.rollback()
        Logger.log_error(ex.args[0])
        return messenger.raise_notification(
            False, 'Įvyko vidinė klaida:' + ex.args[0])
    finally:
        session.close()
Esempio n. 4
0
def save_device_aes_interval(device_id, interval):
    try:
        session = create_user_session()
        main_db_session = db.session.session_factory()
        interval = None if interval == "" else int(interval)

        device = session.query(
            models.UserDevices).filter_by(id=device_id).first()
        device.aes_key_interval = interval

        device_in_main = main_db_session.query(
            models.Devices).filter_by(mac=device.mac).first()
        device_in_main.aes_key_change_date = None if interval == None else datetime.now(
        ) + timedelta(seconds=interval)

        main_db_session.commit()
        session.commit()

        return messenger.raise_notification(
            True, 'AES rakto keitimo intervalas išsaugotas!')
    except Exception as ex:
        Logger.log_error(ex.args[0])
        flash('Nenumatyta klaida', 'danger')
        session.rollback()
        main_db_session.rollback()
    finally:
        session.close()
        main_db_session.close()
Esempio n. 5
0
def user_loader(username):
    if not User.exist_in_db(username):
        Logger.debug('user does not exist in user_loader')
        return
    else:
        user = User.get(username)
        return user
Esempio n. 6
0
def save_device_config(form, device, config_uuid):
    try:
        session = create_user_session()

        device_type = select_device_type(device)
        device_config = device_type.save_configuration(session, form,
                                                       device.id, config_uuid)

        if (config_uuid is None):
            session.add(device_config)

        if (device_config.job_state != enums.ConfigJobState.Running.value):
            response = configure_device_jobs(device, device_config,
                                             device_config.is_active)
            if (not response):
                session.rollback()
            else:
                session.commit()
                flash('Konfigūracija "' + device_config.name + '" išsaugota!',
                      'success')
        else:
            session.rollback()
            return flash(
                'Rutininis darbas vykdomas pagal šią konfigūraciją! Atšaukite rutininį darbą!',
                'danger')

    except Exception as ex:
        Logger.log_error(ex.args[0])
        flash(
            'Nenumatyta klaida išsaugant "' + device_config.name +
            '" prietaisą! Klaida: ' + ex.args[0], 'danger')
        session.rollback()
    finally:
        session.close()
Esempio n. 7
0
    def auth(username, password):
        if User.exist_in_db(username):
            users = db.users
            result = users.find_one({'username': username}, {'password':1, '_id': 0})
            Logger.debug(result['password'])
            if password != result['password']:
                return {'status': False, 'cause': 'wrong password'}
            else:
                return {'status': True}

        else:
            return {'status': False, 'cause': 'username does not exist'}
Esempio n. 8
0
def stop_job(device, config_uuid):
    try:
        session = create_user_session()

        device_type = select_device_type(device)
        device_config = device_type.get_device_config_uuid(
            session, config_uuid)

        response = execute_device_action(device.id, "STOP JOB")

        main_db_session = db.session.session_factory()
        main_db_device = main_db_session.query(models.Devices).filter_by(
            mac=device.mac, user_id=current_user.id).first()

        if (main_db_device is None):  # jei prietaisas nepriskirtas useriui
            return JsonParse.decode_jsonify(
                jsonify(
                    success=False,
                    message=
                    "Naudotojas neturi priskirto prietaiso pagrindinėje duomenų bazėje!"
                ))

        job = main_db_session.query(
            models.DeviceJobs).filter_by(config_uuid=device_config.uuid,
                                         device_id=main_db_device.id).first()
        if (job is not None):
            job.running = False
            job.finish_time = None
            main_db_session.commit()
        else:
            return JsonParse.decode_jsonify(
                jsonify(success=False, message="Rutininis darbas nerastas!"))

        device_config.job_state = enums.ConfigJobState.Idle.value

        save_device_history(session=session,
                            device=device,
                            text="Rutininis darbas pagal konfigūraciją \"" +
                            device_config.name +
                            "\" sustabdytas naudotojo iniciatyva!")

        session.commit()
        return response
    except Exception as ex:
        session.rollback()
        main_db_session.rollback()
        Logger.log_error(ex.args[0])
        return messenger.raise_notification(
            False, 'Įvyko vidinė klaida:' + ex.args[0])
    finally:
        main_db_session.close()
        session.close()
Esempio n. 9
0
def activate_device_configuration(device, config_uuid):
    try:
        session = create_user_session()

        device_type = select_device_type(device)
        device_config = device_type.get_device_config_uuid(
            session, config_uuid)

        if (device_config.job_state != enums.ConfigJobState.Running.value):
            if (device_config.is_active):
                device_config.is_active = enums.ConfigState.Disabled.value
                device_config.job_state = None

                response = configure_device_jobs(device, device_config, False)

                if (not response.success):
                    session.rollback()
                    return messenger.raise_notification(
                        response.success, response.message)

                session.commit()
                return messenger.raise_notification(
                    True,
                    'Konfigūracija "' + device_config.name + '" deaktyvuota!')
            else:
                device_config.is_active = enums.ConfigState.Active.value
                device_config.job_state = enums.ConfigJobState.Idle.value
                response = configure_device_jobs(device, device_config, True)

                if (not response.success):
                    session.rollback()
                    return messenger.raise_notification(
                        response.success, response.message)

                session.commit()
                return messenger.raise_notification(
                    True,
                    'Konfigūracija "' + device_config.name + '" aktyvuota!')
        else:
            session.rollback()
            return messenger.raise_notification(
                False,
                'Rutininis darbas vykdomas pagal šią konfigūraciją! Atšaukite rutininį darbą!'
            )

    except Exception as ex:
        session.rollback()
        Logger.log_error(ex.args[0])
        return messenger.raise_notification(
            False, 'Įvyko vidinė klaida:' + ex.args[0])
    finally:
        session.close()
Esempio n. 10
0
def signup_post():
    try:
        email = request.form.get('email')
        name = request.form.get('name')
        password = request.form.get('password')
        repeat_password = request.form.get('repeat_password')

        session = db.session.session_factory()

        if (not email):
            flash('Neįvedėte naujo naudotojo elektroninio pašto adreso!',
                  'danger')
            return redirect(url_for('auth.signup'))

        if (not name):
            flash('Neįvedėte naujo naudotojo vardo!', 'danger')
            return redirect(url_for('auth.signup'))

        if (password != repeat_password):
            flash('Slaptažodžiai nesutampa!', 'danger')
            return redirect(url_for('auth.signup'))

        if (not password):
            flash('Neįvedėte naujo naudotojo slaptažodžio!', 'danger')
            return redirect(url_for('auth.signup'))

        user = session.query(Users).filter_by(email=email).first()

        if (user):
            flash('Naudotojas su tokiu elektroniniu paštu jau egzistuoja!',
                  'danger')
            return redirect(url_for('auth.signup'))

        new_user = Users(email=email,
                         name=name,
                         password=generate_password_hash(password,
                                                         method='sha256'))

        if (new_user.name == 'Admin'
                and session.query(Users).filter_by(name=new_user.name).first()
                is None):
            new_user.is_admin = True

        session.add(new_user)
        session.commit()

        return redirect(url_for('auth.login'))
    except Exception as ex:
        Logger.log_error(ex.args[0])
    finally:
        session.close()
Esempio n. 11
0
 def config_log(self):
     self._make(PROJ.LOG_DIR)
     if appOptions.log_level in self.sys_argv and \
             self.option_args.pyauto_log_level != self.log_level:
         self.log_level = self.option_args.log_level
     if '--log-file' in self.pytest_args:
         self.log_file_set = True
     else:
         self.log_file_set = False
     Logger.init_logging(
         level=self.log_level,
         mode='rt',
         # log_path=str(PROJ.LOG_DIR/Path(f'app_{self.test_time}'))
     )
     return self
Esempio n. 12
0
def save_device_form(form):
    try:
        session = db.session.session_factory()
        session.autocommit = False  #neleis daryti auto commit, po kiekvieno objekto pakeitimo bus daromas flush.
        session.autoflush = True

        id = int(form.id.data
                 ) if form.id.data is not None and form.id.data != '' else None

        if (id is None):

            deviceObj = Devices(mac=form.mac.data)

            device = session.query(Devices).filter(
                Devices.mac == form.mac.data).first()
            if (device is not None):
                raise Exception(
                    "Prietaisas su tokiu MAC adresu jau egzistuoja!")

            service = DevicesService()
            response = service.generate_device_keys(mac=deviceObj.mac,
                                                    uuid=deviceObj.uuid)

            if (not response.success):
                raise Exception(response.message)

            session.add(deviceObj)
            flash('Prietaisas MAC: ' + deviceObj.mac + ' sukurtas!', 'success')
        else:
            device = session.query(Devices).filter(Devices.id == id).first()
            device.mac = form.mac.data

        session.commit()
    except Exception as ex:
        session.rollback()
        Logger.log_error(ex.args[0])
        session.close()
        flash(ex.args[0], 'danger')
        return False
    finally:
        session.close()
        return True
Esempio n. 13
0
def edit_device(id, form):
    session = create_user_session()

    try:
        device = session.query(models.UserDevices).filter_by(id=id).first()

        if (device is not None):
            device.device_name = form.device_name.data
            device.device_type = form.device_type.data.value
        else:
            flash("Prietaisas neegzistuoja!", 'danger')

        session.commit()
        flash("Įrenginys išsaugotas!", 'success')
    except Exception as Ex:
        Logger.log_error(Ex.args[0])
        flash('Nenumatyta klaida: ' + Ex.args[0], 'danger')

    finally:
        session.close()
Esempio n. 14
0
def get_device_configurations(device_id):
    try:
        session = create_user_session()
        device = session.query(
            models.UserDevices).filter_by(id=device_id).first()

        config_objects_list = []
        device_type = select_device_type(device)
        config_objects_list = device_type.get_configuration_view_list(
            session, device.id)

        session.close()
        return jsonify(
            {'data': [result.serialize for result in config_objects_list]})

    except Exception as ex:
        Logger.log_error(ex.args[0])
        return messenger.raise_notification(
            False, 'Įvyko vidinė klaida:' + ex.args[0])
    finally:
        session.close()
Esempio n. 15
0
def login():
    #
    #
    # if request.method == 'GET':
    #     # if g.user is not None and g.user.is_authenticated:
    #         # return jsonify({'status': False, 'cause': 'already logged in'})
    #     Logger.info('login GET')
    #     return jsonify({'status': False, 'cause': 'only POST is allowed'})
    #     # return  '''
    #     # <!doctype html>
    #     # <title>Login</title>
    #     # <h1>login</h1>
    #     # <form action="" method=post enctype=multipart/form-data>
    #     #   <p><input type=text name=username>
    #     #     <input type=password name=password>
    #     #      <input type=submit value=login>
    #     # </form>
    #     # '''

    username = request.json['username']
    password = request.json['password']
    Logger.info('login POST')

    auth_result = User.auth(username, password)
    if auth_result['status']:
        Logger.debug('before login_user')
        user = User.get(username)
        flask_login.login_user(user)
        Logger.debug('after login_user')
        return flask.jsonify({'status': True})
    else:
        return flask.jsonify(auth_result)
Esempio n. 16
0
def execute_device_command(id, command):
    if current_user.is_authenticated:
        try:
            session = create_user_session()
            device = session.query(models.UserDevices).filter_by(id=id).first()

            systemName = "system"
            topic = current_user.uuid + "/" + systemName + "/" + device.mac + "/control"
            response_topic = topic + "/response"

            service = MqttService()
            # publishinam komanda
            response = service.publish_with_response(topic, response_topic,
                                                     command, 10, device.mac)

            if (response.success):
                return response.message
            if (response.success is False
                    and response.reason == "Time is up."):
                device.status = enums.DeviceState.Offline.value
                session.commit()
                Logger.log_error(response.reason)
                flash('Serviso klaida:' + response.reason, 'danger')
                return -1
            else:
                flash('Įvyko vidinė klaida', 'danger')
                return -1

        except Exception as ex:
            session.rollback()
            Logger.log_error(ex.args[0])
            flash('Įvyko vidinė klaida:' + ex.args[0], 'danger')
            return -1
        finally:
            session.close()

    else:
        flash('Naudotojas nėra autentifikuotas!', 'danger')
        return -1
Esempio n. 17
0
 def process(self, number='90'):
     registered = self.adapter.number_exists(number)
     Logger.log("number registered %s " % registered)
     count = 0
     current_poll = self.adapter.get_current_poll(number)
     while (current_poll):
         Logger.log(current_poll)
         # print current_poll["question"]
         if current_poll['type'] != 'none':
             user_response = steps[count]
             count += 1
             post_response = self.adapter.respond_to_poll(number, current_poll, user_response)
             Logger.log(post_response)
             # print(post_response["result"]["response"])
         current_poll = self.adapter.get_current_poll(number)
Esempio n. 18
0
def send_device_configuration(device_id, data_type, data):
    if current_user.is_authenticated:
        try:
            session = create_user_session()
            device = session.query(
                models.UserDevices).filter_by(id=device_id).first()

            if (device.status != enums.DeviceState.Active.value
                    and device.status != enums.DeviceState.Registered.value):
                return messenger.raise_notification(
                    False,
                    'Prietaisas nėra aktyvus! Negalima operacija!',
                )

            # parenkamos temos configo siuntimui ir atsakymo gavimui
            systemName = "system"
            topic = current_user.uuid + "/" + systemName + "/" + device.mac + "/setconfig"
            response_topic = topic + "/response"

            payload = None

            # common commands
            if (data_type == 'interval'):
                payload = "delay=" + data
            else:
                return messenger.raise_notification(False,
                                                    'Komanda netinkamo tipo!')

            service = MqttService()
            # publishinam komanda
            response = service.publish_with_response(topic, response_topic,
                                                     payload, 10, device.mac)

            if (response.success):
                if (data_type == 'interval'):
                    device.publish_interval = data
                    session.commit()
                return messenger.raise_notification(
                    True, 'Komanda įvykdyta sėkmingai!')
            if (response.success is False
                    and response.reason == "Time is up."):
                device.status = enums.DeviceState.Offline.value
                session.commit()
                Logger.log_error(response.reason)
                return messenger.raise_notification(
                    False,
                    'Baigėsi laikas! Komunikacijos su prietaisu klaida.')
            else:
                return messenger.raise_notification(
                    False, 'Komanda nebuvo įvykdyta.')

        except Exception as ex:
            session.rollback()
            Logger.log_error(ex.args[0])
            return messenger.raise_notification(
                False, 'Įvyko vidinė klaida:' + ex.args[0])
        finally:
            session.close()

    else:
        return messenger.raise_notification(
            False, 'Naudotojas nėra autentifikuotas!')
Esempio n. 19
0
def execute_device_action(id, command):
    if current_user.is_authenticated:
        try:
            session = create_user_session()
            device = session.query(models.UserDevices).filter_by(id=id).first()

            if (device.status != enums.DeviceState.Active.value
                    and device.status != enums.DeviceState.Registered.value
                    and command != 'REBOOT'):
                return messenger.raise_notification(
                    False,
                    'Prietaisas nėra aktyvus! Negalima operacija!',
                )

            systemName = "system"
            topic = current_user.uuid + "/" + systemName + "/" + device.mac + "/control"
            response_topic = topic + "/response"

            payload = None

            # komandos parinkimas
            device_type = select_device_type(device)
            payload = device_type.form_mqtt_payload(command)

            # common commands
            if (command == 'REBOOT'):
                device.status = enums.DeviceState.Rebooting.value
                session.commit()
                payload = "reboot"

            service = MqttService()
            # publishinam komanda
            response = service.publish_with_response(topic, response_topic,
                                                     payload, 10, device.mac)

            if (response.success):
                return messenger.raise_notification(
                    True, 'Komanda įvykdyta sėkmingai!')
            if (response.success is False
                    and response.reason == "Time is up."):
                device.status = enums.DeviceState.Offline.value
                session.commit()
                Logger.log_error(response.reason)
                return messenger.raise_notification(
                    False,
                    'Baigėsi laikas! Komunikacijos su prietaisu klaida.')
            else:
                return messenger.raise_notification(
                    False, 'Komanda nebuvo įvykdyta.')

        except Exception as ex:
            session.rollback()
            Logger.log_error(ex.args[0])
            return messenger.raise_notification(
                False, 'Įvyko vidinė klaida:' + ex.args[0])
        finally:
            session.close()

    else:
        return messenger.raise_notification(
            False, 'Naudotojas nėra autentifikuotas!')
Esempio n. 20
0
def pytest_sessionstart(session):
    from app import Logger
    Logger.init_logging(log_path=session.config.getoption('--log-file'))
Esempio n. 21
0
def add_new_device(code, device_name, device_type):
    session = create_user_session()
    main_db_session = db.session.session_factory()

    try:
        # sukuriam prietaisu lenta, jei tokios nera
        if not table_exists("user_devices"):
            models.UserDevices.__table__.create(session.bind)
            session.commit()

        if not table_exists("user_device_history"):
            models.UserDeviceHistory.__table__.create(session.bind)
            session.commit()

        # patikrinam, ar toks prietaisas egzistuoja MainDB
        device_in_main = main_db_session.query(
            models.Devices).filter_by(uuid=code).first()

        if device_in_main is None:
            flash("Toks prietaisas neegzistuoja!", "danger")
            session.rollback()
            main_db_session.rollback()
        elif device_in_main.user_id is not None and device_in_main.user_id != current_user.id:
            flash("Prietaisas priklauso kitam naudotojui!", "danger")
            session.rollback()
            main_db_session.rollback()
        elif device_in_main.user_id is None:
            device = session.query(
                models.UserDevices).filter_by(mac=device_in_main.mac).first()
            # pridedam prietaisa i userio DB
            if not device:
                new_device = models.UserDevices(
                    device_name=device_name,
                    mac=device_in_main.mac,
                    status=enums.DeviceState.Registered.value,
                    device_type=device_type,
                    publish_interval=30,
                    aes_key_interval=None)

                session.add(new_device)

                device_in_main.user_id = current_user.id

                device_type = select_device_type(new_device)

                device_type.create_tables(session)

                # konfiguruojam devaisa. Nustatome devaiso confige naudotojo uuid
                systemName = "system"
                defaultUUID = "00000000-0000-0000-0000-000000000000"

                payload = "useruuid=" + current_user.uuid
                topic = defaultUUID + "/" + systemName + "/" + device_in_main.mac + "/setconfig"
                response_topic = topic + "/response"

                service = MqttService()

                response = service.publish_with_response(
                    topic, response_topic, payload, 10, new_device.mac)

                if (response.success):
                    session.commit()
                    main_db_session.commit()
                    flash('Prietaisas sėkmingai užregistruotas!', 'success')
                else:
                    flash('Prietaisas neužregistruotas!', 'danger')
            else:
                flash("Toks prietaisas jau pridėtas!", "danger")
                session.rollback()
                main_db_session.rollback()
        else:
            flash("Toks prietaisas jau pridėtas!", "danger")
            session.rollback()
            main_db_session.rollback()

    except Exception as Ex:
        Logger.log_error(Ex.args)
        flash('Nenumatyta klaida: ' + Ex.args, 'danger')

    finally:
        session.close()
Esempio n. 22
0
def configure_device_jobs(device, device_config, is_active):
    try:
        main_db_session = db.session.session_factory()
        main_db_device = main_db_session.query(models.Devices).filter_by(
            mac=device.mac, user_id=current_user.id).first()

        if (main_db_device is None):  # jei prietaisas nepriskirtas useriui
            return JsonParse.decode_jsonify(
                jsonify(
                    success=False,
                    message=
                    "Naudotojas neturi priskirto prietaiso pagrindinėje duomenų bazėje!"
                ))

        if (is_active):
            job = main_db_session.query(models.DeviceJobs).filter_by(
                config_uuid=device_config.uuid,
                device_id=main_db_device.id).first()

            if (job is None):  # jei jobas neegzistuoja, kuriam nauja
                job = models.DeviceJobs(device_id=main_db_device.id,
                                        start_time=device_config.start_time,
                                        duration=device_config.duration,
                                        weekdays=device_config.weekdays,
                                        config_uuid=device_config.uuid,
                                        running=False)
                device_config.job_state = enums.ConfigJobState.Idle.value
                main_db_session.add(job)
            else:
                if (job.running == False):
                    job.device_id = main_db_device.id,
                    job.start_time = device_config.start_time,
                    job.duration = device_config.duration,
                    job.weekdays = device_config.weekdays,
                    job.config_uuid = device_config.uuid
                else:
                    return JsonParse.decode_jsonify(
                        jsonify(
                            success=False,
                            message=
                            "Job\'as šiuo metu dirba! Negalima redaguoti konfigūracijos!"
                        ))
        else:  # jobo naikinimas
            job = main_db_session.query(models.DeviceJobs).filter_by(
                config_uuid=device_config.uuid,
                device_id=main_db_device.id).first()

            device_config.job_state = enums.ConfigJobState.Disabled.value

            if (job is not None):
                main_db_session.delete(job)

        main_db_session.commit()
        return JsonParse.decode_jsonify(jsonify(success=True))
    except Exception as ex:
        Logger.log_error(ex.args[0])
        return JsonParse.decode_jsonify(
            jsonify(success=False,
                    message="Įvyko vidinė klaida:" + ex.args[0]))
    finally:
        main_db_session.close()
Esempio n. 23
0
    if email_count % MAX_EMAIL_PER_BATCH > 0:
        total_batch_count += 1

    # Main Loop
    # while the name_email_dictionary has members
    while name_email_dictionary:
        for i in range(MAX_EMAIL_PER_BATCH):
            # if the dictionary still has members
            if name_email_dictionary:
                # pop an item and append it to the batch list
                email_batch_list.append(name_email_dictionary.popitem())
        # when the batch_list has reached 50 members, run the generate email method
        
        batch_success_data = Mailer.generateEmail(email_batch_list, current_batch, total_batch_count, html_text, plain_text, SMTP_TIME_OUT)
        current_batch += 1

        # update statistics
        for item in batch_success_data['valid_emails']:
            success_data['valid_emails'].append(item)
        for item in batch_success_data['invalid_emails']:
            success_data['invalid_emails'].append(item)
        for item in batch_success_data['send_errors']:
            success_data['send_errors'].append(item)
        success_data['send_count'] += batch_success_data['send_count']

        #reset batch list
        email_batch_list = []
        
    Logger.create_log_file(success_data)