Exemplo n.º 1
0
def note_add(form):
    action = '{action} {controller}'.format(
        action=TRANSLATIONS['add']['title'],
        controller=TRANSLATIONS['note']['title'])
    error = []
    list_tags = []

    new_note = Notes()

    if not form.name.data:
        error.append("Name cannot be left blank")
    if not form.note_tags.data:
        error.append("At least one tag must be selected")
    if not form.note.data:
        error.append("Note cannot be left blank")

    try:
        for each_tag in form.note_tags.data:
            check_tag = NoteTags.query.filter(
                NoteTags.unique_id == each_tag).first()
            if not check_tag:
                error.append("Invalid tag: {}".format(each_tag))
            else:
                list_tags.append(check_tag.unique_id)
        new_note.tags = ",".join(list_tags)
    except Exception as msg:
        error.append("Invalid tag format: {}".format(msg))

    if form.enter_custom_date_time.data:
        try:
            new_note.date_time = datetime_time_to_utc(form.date_time.data)
        except Exception as msg:
            error.append("Error while parsing date/time: {}".format(msg))

    if form.files.data:
        new_note.unique_id = set_uuid()
        assure_path_exists(PATH_NOTE_ATTACHMENTS)
        filename_list = []
        for each_file in form.files.raw_data:
            file_name = "{pre}_{name}".format(pre=new_note.unique_id,
                                              name=each_file.filename)
            file_save_path = os.path.join(PATH_NOTE_ATTACHMENTS, file_name)
            each_file.save(file_save_path)
            filename_list.append(file_name)
        new_note.files = ",".join(filename_list)

    if not error:
        new_note.name = form.name.data
        new_note.note = form.note.data
        new_note.save()

    flash_success_errors(error, action, url_for('routes_page.page_notes'))
Exemplo n.º 2
0
def note_add(form):
    action = '{action} {controller}'.format(
        action=TRANSLATIONS['add']['title'],
        controller=TRANSLATIONS['note']['title'])
    error = []
    list_tags = []

    new_note = Notes()

    if not form.name.data:
        error.append("Name cannot be left blank")
    if not form.note_tags.data:
        error.append("At least one tag must be selected")
    if not form.note.data:
        error.append("Note cannot be left blank")

    try:
        for each_tag in form.note_tags.data:
            check_tag = NoteTags.query.filter(
                NoteTags.unique_id == each_tag).first()
            if not check_tag:
                error.append("Invalid tag: {}".format(each_tag))
            else:
                list_tags.append(check_tag.unique_id)
        new_note.tags = ",".join(list_tags)
    except Exception as msg:
        error.append("Invalid tag format: {}".format(msg))

    if form.enter_custom_date_time.data:
        try:
            new_note.date_time = datetime_time_to_utc(form.date_time.data)
        except Exception as msg:
            error.append("Error while parsing date/time: {}".format(msg))

    if form.files.data:
        new_note.unique_id = set_uuid()
        assure_path_exists(PATH_NOTE_ATTACHMENTS)
        filename_list = []
        for each_file in form.files.raw_data:
            file_name = "{pre}_{name}".format(
                pre=new_note.unique_id, name=each_file.filename)
            file_save_path = os.path.join(PATH_NOTE_ATTACHMENTS, file_name)
            each_file.save(file_save_path)
            filename_list.append(file_name)
        new_note.files = ",".join(filename_list)

    if not error:
        new_note.name = form.name.data
        new_note.note = form.note.data
        new_note.save()

    flash_success_errors(error, action, url_for('routes_page.page_notes'))
Exemplo n.º 3
0
def action_create_note(cond_action, message, single_action, note_tags):
    tag_name = db_retrieve_table_daemon(
        NoteTags, unique_id=cond_action.do_action_string).name

    message += " Create note with tag '{}'.".format(tag_name)
    if single_action and cond_action.do_action_string:
        list_tags = []
        check_tag = db_retrieve_table_daemon(
            NoteTags, unique_id=cond_action.do_action_string)
        if check_tag:
            list_tags.append(cond_action.do_action_string)

        if list_tags:
            with session_scope(MYCODO_DB_PATH) as db_session:
                new_note = Notes()
                new_note.name = 'Action'
                new_note.tags = ','.join(list_tags)
                new_note.note = message
                db_session.add(new_note)
    else:
        note_tags.append(cond_action.do_action_string)
    return message, note_tags
Exemplo n.º 4
0
def trigger_function_actions(function_id, message='', debug=False):
    """
    Execute the Actions belonging to a particular Function

    :param function_id:
    :param message: The message generated from the conditional check
    :param debug: determine if logging level should be DEBUG
    :return:
    """
    logger_actions = logging.getLogger(
        "mycodo.trigger_function_actions_{id}".format(
            id=function_id.split('-')[0]))

    if debug:
        logger_actions.setLevel(logging.DEBUG)
    else:
        logger_actions.setLevel(logging.INFO)

    # List of all email notification recipients
    # List is appended with TO email addresses when an email Action is
    # encountered. An email is sent to all recipients after all actions
    # have been executed.
    email_recipients = []

    # List of tags to add to a note
    note_tags = []

    attachment_file = None
    attachment_type = None

    actions = db_retrieve_table_daemon(Actions)
    actions = actions.filter(
        Actions.function_id == function_id).all()

    for cond_action in actions:
        (message,
         note_tags,
         email_recipients,
         attachment_file,
         attachment_type) = trigger_action(
            cond_action.unique_id,
            message=message,
            single_action=False,
            note_tags=note_tags,
            email_recipients=email_recipients,
            attachment_file=attachment_file,
            attachment_type=attachment_type,
            debug=debug)

    # Send email after all conditional actions have been checked
    # In order to append all action messages to send in the email
    # send_email_at_end will be None or the TO email address
    if email_recipients:
        # If the emails per hour limit has not been exceeded
        smtp_wait_timer, allowed_to_send_notice = check_allowed_to_email()
        if allowed_to_send_notice:
            smtp = db_retrieve_table_daemon(SMTP, entry='first')
            send_email(smtp.host, smtp.ssl, smtp.port,
                       smtp.user, smtp.passw, smtp.email_from,
                       email_recipients, message,
                       attachment_file, attachment_type)
        else:
            logger_actions.error(
                "Wait {sec:.0f} seconds to email again.".format(
                    sec=smtp_wait_timer - time.time()))

    # Create a note with the tags from the unique_ids in the list note_tags
    if note_tags:
        list_tags = []
        for each_note_tag_id in note_tags:
            check_tag = db_retrieve_table_daemon(
                NoteTags, unique_id=each_note_tag_id)
            if check_tag:
                list_tags.append(each_note_tag_id)
        if list_tags:
            with session_scope(MYCODO_DB_PATH) as db_session:
                new_note = Notes()
                new_note.name = 'Action'
                new_note.tags = ','.join(list_tags)
                new_note.note = message
                db_session.add(new_note)

    logger_actions.debug("Message: {}".format(message))
Exemplo n.º 5
0
def trigger_function_actions(function_id,
                             message='',
                             last_measurement=None,
                             device_id=None,
                             device_measurement=None,
                             edge=None,
                             output_state=None,
                             on_duration=None,
                             duty_cycle=None):
    """
    Execute the Actions belonging to a particular Function

    :param function_id:
    :param device_id: The unique ID associated with the device_measurement
    :param message: The message generated from the conditional check
    :param last_measurement: The last measurement value
    :param device_measurement: The measurement (i.e. "temperature")
    :param edge: If edge conditional, rise/on (1) or fall/off (0)
    :param output_state: If output conditional, the output state (on/off) to trigger the action
    :param on_duration: If output conditional, the ON duration
    :param duty_cycle: If output conditional, the duty cycle
    :return:
    """
    logger_actions = logging.getLogger(
        "mycodo.trigger_function_actions_{id}".format(
            id=function_id.split('-')[0]))

    # List of all email notification recipients
    # List is appended with TO email addresses when an email Action is
    # encountered. An email is sent to all recipients after all actions
    # have been executed.
    email_recipients = []

    # List of tags to add to a note
    note_tags = []

    attachment_file = None
    attachment_type = None
    input_dev = None
    output = None
    device = None

    actions = db_retrieve_table_daemon(Actions)
    actions = actions.filter(Actions.function_id == function_id).all()

    if device_id:
        input_dev = db_retrieve_table_daemon(Input,
                                             unique_id=device_id,
                                             entry='first')
        if input_dev:
            device = input_dev

        math = db_retrieve_table_daemon(Math,
                                        unique_id=device_id,
                                        entry='first')
        if math:
            device = math

        output = db_retrieve_table_daemon(Output,
                                          unique_id=device_id,
                                          entry='first')
        if output:
            device = output

        pid = db_retrieve_table_daemon(PID, unique_id=device_id, entry='first')
        if pid:
            device = pid

    for cond_action in actions:
        (message, note_tags, email_recipients, attachment_file,
         attachment_type) = trigger_action(cond_action.unique_id,
                                           message=message,
                                           single_action=False,
                                           note_tags=note_tags,
                                           email_recipients=email_recipients,
                                           attachment_file=attachment_file,
                                           attachment_type=attachment_type)

    # Send email after all conditional actions have been checked
    # In order to append all action messages to send in the email
    # send_email_at_end will be None or the TO email address
    if email_recipients:
        smtp = db_retrieve_table_daemon(SMTP, entry='first')
        send_email(smtp.host, smtp.ssl, smtp.port, smtp.user, smtp.passw,
                   smtp.email_from, email_recipients, message, attachment_file,
                   attachment_type)

    # Create a note with the tags from the unique_ids in the list note_tags
    if note_tags:
        list_tags = []
        for each_note_tag_id in note_tags:
            check_tag = db_retrieve_table_daemon(NoteTags,
                                                 unique_id=each_note_tag_id)
            if check_tag:
                list_tags.append(each_note_tag_id)
        if list_tags:
            with session_scope(MYCODO_DB_PATH) as db_session:
                new_note = Notes()
                new_note.name = 'Action'
                new_note.tags = ','.join(list_tags)
                new_note.note = message
                db_session.add(new_note)

    logger_actions.debug(message)
Exemplo n.º 6
0
def trigger_action(cond_action_id,
                   message='',
                   note_tags=None,
                   email_recipients=None,
                   attachment_file=None,
                   attachment_type=None,
                   single_action=False):
    cond_action = db_retrieve_table_daemon(Actions, unique_id=cond_action_id)
    if not cond_action:
        return

    logger_actions = logging.getLogger("mycodo.trigger_action_{id}".format(
        id=cond_action.unique_id.split('-')[0]))

    control = DaemonControl()

    smtp_table = db_retrieve_table_daemon(SMTP, entry='first')
    smtp_max_count = smtp_table.hourly_max
    smtp_wait_timer = smtp_table.smtp_wait_timer
    email_count = smtp_table.email_count

    message += "\n[Action {id}]:".format(
        id=cond_action.unique_id.split('-')[0],
        action_type=cond_action.action_type)

    # Pause
    if cond_action.action_type == 'pause_actions':
        message += " [{id}] Pause actions for {sec} seconds.".format(
            id=cond_action.id, sec=cond_action.pause_duration)

        time.sleep(cond_action.pause_duration)

    # Actuate output (duration)
    if (cond_action.action_type == 'output' and cond_action.do_unique_id
            and cond_action.do_output_state in ['on', 'off']):
        this_output = db_retrieve_table_daemon(
            Output, unique_id=cond_action.do_unique_id, entry='first')
        message += " Turn output {unique_id} ({id}, {name}) {state}".format(
            unique_id=cond_action.do_unique_id,
            id=this_output.id,
            name=this_output.name,
            state=cond_action.do_output_state)
        if (cond_action.do_output_state == 'on'
                and cond_action.do_output_duration):
            message += " for {sec} seconds".format(
                sec=cond_action.do_output_duration)
        message += "."

        output_on_off = threading.Thread(
            target=control.output_on_off,
            args=(
                cond_action.do_unique_id,
                cond_action.do_output_state,
            ),
            kwargs={'duration': cond_action.do_output_duration})
        output_on_off.start()

    # Actuate output (PWM)
    if (cond_action.action_type == 'output_pwm' and cond_action.do_unique_id
            and cond_action.do_output_pwm):
        this_output = db_retrieve_table_daemon(
            Output, unique_id=cond_action.do_unique_id, entry='first')
        message += " Turn output {unique_id} ({id}, {name}) duty cycle to {duty_cycle}%.".format(
            unique_id=cond_action.do_unique_id,
            id=this_output.id,
            name=this_output.name,
            duty_cycle=cond_action.do_output_pwm)

        output_on = threading.Thread(
            target=control.output_on,
            args=(cond_action.do_unique_id, ),
            kwargs={'duty_cycle': cond_action.do_output_pwm})
        output_on.start()

    # Execute command in shell
    if cond_action.action_type == 'command':

        # Replace string variables with actual values
        command_str = cond_action.do_action_string

        # TODO: Maybe get this working again with the new measurement system
        # # Replace measurement variables
        # if last_measurement:
        #     command_str = command_str.replace(
        #         "((measure_{var}))".format(
        #             var=device_measurement), str(last_measurement))
        # if device and device.period:
        #     command_str = command_str.replace(
        #         "((measure_period))", str(device.period))
        # if input_dev:
        #     command_str = command_str.replace(
        #         "((measure_location))", str(input_dev.location))
        # if input_dev and device_measurement == input_dev.measurements:
        #     command_str = command_str.replace(
        #         "((measure_linux_command))", str(last_measurement))
        #
        # # Replace output variables
        # if output:
        #     if output.pin:
        #         command_str = command_str.replace(
        #             "((output_pin))", str(output.pin))
        #     if output_state:
        #         command_str = command_str.replace(
        #             "((output_action))", str(output_state))
        #     if on_duration:
        #         command_str = command_str.replace(
        #             "((output_duration))", str(on_duration))
        #     if duty_cycle:
        #         command_str = command_str.replace(
        #             "((output_pwm))", str(duty_cycle))
        #
        # # Replace edge variables
        # if edge:
        #     command_str = command_str.replace(
        #         "((edge_state))", str(edge))

        message += " Execute '{com}' ".format(com=command_str)

        _, _, cmd_status = cmd_output(command_str)

        message += "(return status: {stat}).".format(stat=cmd_status)

    # Create Note
    if cond_action.action_type == 'create_note':
        tag_name = db_retrieve_table_daemon(
            NoteTags, unique_id=cond_action.do_action_string).name

        message += " Create note with tag '{}'.".format(tag_name)
        if single_action and cond_action.do_action_string:
            list_tags = []
            check_tag = db_retrieve_table_daemon(
                NoteTags, unique_id=cond_action.do_action_string)
            if check_tag:
                list_tags.append(cond_action.do_action_string)

            if list_tags:
                with session_scope(MYCODO_DB_PATH) as db_session:
                    new_note = Notes()
                    new_note.name = 'Action'
                    new_note.tags = ','.join(list_tags)
                    new_note.note = message
                    db_session.add(new_note)
        else:
            note_tags.append(cond_action.do_action_string)

    # Capture photo
    # If emailing later, store location of photo as attachment_file
    if cond_action.action_type in ['photo', 'photo_email']:
        this_camera = db_retrieve_table_daemon(
            Camera, unique_id=cond_action.do_unique_id, entry='first')
        message += "  Capturing photo with camera {unique_id} ({id}, {name}).".format(
            unique_id=cond_action.do_unique_id,
            id=this_camera.id,
            name=this_camera.name)
        camera_still = db_retrieve_table_daemon(
            Camera, unique_id=cond_action.do_unique_id)
        attachment_path_file = camera_record('photo', camera_still.unique_id)
        attachment_file = os.path.join(attachment_path_file[0],
                                       attachment_path_file[1])

    # Capture video
    if cond_action.action_type in ['video', 'video_email']:
        this_camera = db_retrieve_table_daemon(
            Camera, unique_id=cond_action.do_unique_id, entry='first')
        message += "  Capturing video with camera {unique_id} ({id}, {name}).".format(
            unique_id=cond_action.do_unique_id,
            id=this_camera.id,
            name=this_camera.name)
        camera_stream = db_retrieve_table_daemon(
            Camera, unique_id=cond_action.do_unique_id)
        attachment_path_file = camera_record(
            'video',
            camera_stream.unique_id,
            duration_sec=cond_action.do_camera_duration)
        attachment_file = os.path.join(attachment_path_file[0],
                                       attachment_path_file[1])

    # Activate Controller
    if cond_action.action_type == 'activate_controller':
        (controller_type, controller_object,
         controller_entry) = which_controller(cond_action.do_unique_id)
        message += " Activate Controller {unique_id} ({id}, {name}).".format(
            unique_id=cond_action.do_unique_id,
            id=controller_entry.id,
            name=controller_entry.name)
        if controller_entry.is_activated:
            message += " Notice: Controller is already active!"
        else:
            # If controller is Conditional and is
            # run_pwm_method, activate method start
            is_conditional = db_retrieve_table_daemon(
                Conditional, unique_id=cond_action.do_unique_id, entry='first')
            if (is_conditional
                    and is_conditional.conditional_type == 'run_pwm_method'):
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_cont_ready = new_session.query(Conditional).filter(
                        Conditional.unique_id ==
                        cond_action.do_unique_id).first()
                    mod_cont_ready.method_start_time = 'Ready'
                    new_session.commit()

            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_cont = new_session.query(controller_object).filter(
                    controller_object.unique_id ==
                    cond_action.do_unique_id).first()
                mod_cont.is_activated = True
                new_session.commit()
            activate_controller = threading.Thread(
                target=control.controller_activate,
                args=(
                    controller_type,
                    cond_action.do_unique_id,
                ))
            activate_controller.start()

    # Deactivate Controller
    if cond_action.action_type == 'deactivate_controller':
        (controller_type, controller_object,
         controller_entry) = which_controller(cond_action.do_unique_id)
        message += " Deactivate Controller {unique_id} ({id}, {name}).".format(
            unique_id=cond_action.do_unique_id,
            id=controller_entry.id,
            name=controller_entry.name)
        if not controller_entry.is_activated:
            message += " Notice: Controller is already inactive!"
        else:
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_cont = new_session.query(controller_object).filter(
                    controller_object.unique_id ==
                    cond_action.do_unique_id).first()
                mod_cont.is_activated = False
                new_session.commit()
            deactivate_controller = threading.Thread(
                target=control.controller_deactivate,
                args=(
                    controller_type,
                    cond_action.do_unique_id,
                ))
            deactivate_controller.start()

    # Resume PID controller
    if cond_action.action_type == 'resume_pid':
        pid = db_retrieve_table_daemon(PID,
                                       unique_id=cond_action.do_unique_id,
                                       entry='first')
        message += " Resume PID {unique_id} ({id}, {name}).".format(
            unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name)
        if not pid.is_paused:
            message += " Notice: PID is not paused!"
        elif pid.is_activated:
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_pid = new_session.query(PID).filter(
                    PID.unique_id == cond_action.do_unique_id).first()
                mod_pid.is_paused = False
                new_session.commit()
            resume_pid = threading.Thread(target=control.pid_resume,
                                          args=(cond_action.do_unique_id, ))
            resume_pid.start()

    # Pause PID controller
    if cond_action.action_type == 'pause_pid':
        pid = db_retrieve_table_daemon(PID,
                                       unique_id=cond_action.do_unique_id,
                                       entry='first')
        message += " Pause PID {unique_id} ({id}, {name}).".format(
            unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name)
        if pid.is_paused:
            message += " Notice: PID is already paused!"
        elif pid.is_activated:
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_pid = new_session.query(PID).filter(
                    PID.unique_id == cond_action.do_unique_id).first()
                mod_pid.is_paused = True
                new_session.commit()
            pause_pid = threading.Thread(target=control.pid_pause,
                                         args=(cond_action.do_unique_id, ))
            pause_pid.start()

    # Set PID Setpoint
    if cond_action.action_type == 'setpoint_pid':
        pid = db_retrieve_table_daemon(PID,
                                       unique_id=cond_action.do_unique_id,
                                       entry='first')
        message += " Set Setpoint of PID {unique_id} ({id}, {name}).".format(
            unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name)
        if pid.is_activated:
            setpoint_pid = threading.Thread(
                target=control.pid_set,
                args=(
                    pid.unique_id,
                    'setpoint',
                    float(cond_action.do_action_string),
                ))
            setpoint_pid.start()
        else:
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_pid = new_session.query(PID).filter(
                    PID.unique_id == cond_action.do_unique_id).first()
                mod_pid.setpoint = cond_action.do_action_string
                new_session.commit()

    # Set PID Method and start method from beginning
    if cond_action.action_type == 'method_pid':
        pid = db_retrieve_table_daemon(PID,
                                       unique_id=cond_action.do_unique_id,
                                       entry='first')
        message += " Set Method of PID {unique_id} ({id}, {name}).".format(
            unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name)

        # Instruct method to start
        with session_scope(MYCODO_DB_PATH) as new_session:
            mod_pid = new_session.query(PID).filter(
                PID.unique_id == cond_action.do_unique_id).first()
            mod_pid.method_start_time = 'Ready'
            new_session.commit()

        pid = db_retrieve_table_daemon(PID,
                                       unique_id=cond_action.do_unique_id,
                                       entry='first')
        if pid.is_activated:
            method_pid = threading.Thread(target=control.pid_set,
                                          args=(
                                              pid.unique_id,
                                              'method',
                                              cond_action.do_action_string,
                                          ))
            method_pid.start()
        else:
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_pid = new_session.query(PID).filter(
                    PID.unique_id == cond_action.do_unique_id).first()
                mod_pid.method_id = cond_action.do_action_string
                new_session.commit()

    # Email the Conditional message. Optionally capture a photo or
    # video and attach to the email.
    if cond_action.action_type in ['email', 'photo_email', 'video_email']:

        if (email_count >= smtp_max_count and time.time() < smtp_wait_timer):
            allowed_to_send_notice = False
        else:
            if time.time() > smtp_wait_timer:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_smtp = new_session.query(SMTP).first()
                    mod_smtp.email_count = 0
                    mod_smtp.smtp_wait_timer = time.time() + 3600
                    new_session.commit()
            allowed_to_send_notice = True

        with session_scope(MYCODO_DB_PATH) as new_session:
            mod_smtp = new_session.query(SMTP).first()
            mod_smtp.email_count += 1
            new_session.commit()

        # If the emails per hour limit has not been exceeded
        if allowed_to_send_notice:
            message += " Notify {email}.".format(
                email=cond_action.do_action_string)
            # attachment_type != False indicates to
            # attach a photo or video
            if cond_action.action_type == 'photo_email':
                message += " Photo attached to email."
                attachment_type = 'still'
            elif cond_action.action_type == 'video_email':
                message += " Video attached to email."
                attachment_type = 'video'

            if single_action and cond_action.do_action_string:
                smtp = db_retrieve_table_daemon(SMTP, entry='first')
                send_email(smtp.host, smtp.ssl, smtp.port, smtp.user,
                           smtp.passw, smtp.email_from,
                           [cond_action.do_action_string], message,
                           attachment_file, attachment_type)
            else:
                email_recipients.append(cond_action.do_action_string)
        else:
            logger_actions.error(
                "Wait {sec:.0f} seconds to email again.".format(
                    sec=smtp_wait_timer - time.time()))

    if cond_action.action_type == 'flash_lcd_on':
        lcd = db_retrieve_table_daemon(LCD, unique_id=cond_action.do_unique_id)
        message += " LCD {unique_id} ({id}, {name}) Flash On.".format(
            unique_id=cond_action.do_unique_id, id=lcd.id, name=lcd.name)

        start_flashing = threading.Thread(target=control.lcd_flash,
                                          args=(
                                              cond_action.do_unique_id,
                                              True,
                                          ))
        start_flashing.start()

    if cond_action.action_type == 'flash_lcd_off':
        lcd = db_retrieve_table_daemon(LCD, unique_id=cond_action.do_unique_id)
        message += " LCD {unique_id} ({id}, {name}) Flash Off.".format(
            unique_id=cond_action.do_unique_id, id=lcd.id, name=lcd.name)

        start_flashing = threading.Thread(target=control.lcd_flash,
                                          args=(
                                              cond_action.do_unique_id,
                                              False,
                                          ))
        start_flashing.start()

    if cond_action.action_type == 'lcd_backlight_off':
        lcd = db_retrieve_table_daemon(LCD, unique_id=cond_action.do_unique_id)
        message += " LCD {unique_id} ({id}, {name}) Backlight Off.".format(
            unique_id=cond_action.do_unique_id, id=lcd.id, name=lcd.name)

        start_flashing = threading.Thread(target=control.lcd_backlight,
                                          args=(
                                              cond_action.do_unique_id,
                                              False,
                                          ))
        start_flashing.start()

    if cond_action.action_type == 'lcd_backlight_on':
        lcd = db_retrieve_table_daemon(LCD, unique_id=cond_action.do_unique_id)
        message += " LCD {unique_id} ({id}, {name}) Backlight On.".format(
            unique_id=cond_action.do_unique_id, id=lcd.id, name=lcd.name)

        start_flashing = threading.Thread(target=control.lcd_backlight,
                                          args=(
                                              cond_action.do_unique_id,
                                              True,
                                          ))
        start_flashing.start()

    if single_action:
        return message
    else:
        return message, note_tags, email_recipients, attachment_file, attachment_type
Exemplo n.º 7
0
def trigger_function_actions(function_id, message=''):
    """
    Execute the Actions belonging to a particular Function

    :param function_id:
    :param message: The message generated from the conditional check
    :return:
    """
    logger_actions = logging.getLogger(
        "mycodo.trigger_function_actions_{id}".format(
            id=function_id.split('-')[0]))

    # List of all email notification recipients
    # List is appended with TO email addresses when an email Action is
    # encountered. An email is sent to all recipients after all actions
    # have been executed.
    email_recipients = []

    # List of tags to add to a note
    note_tags = []

    attachment_file = None
    attachment_type = None

    actions = db_retrieve_table_daemon(Actions)
    actions = actions.filter(Actions.function_id == function_id).all()

    for cond_action in actions:
        (message, note_tags, email_recipients, attachment_file,
         attachment_type) = trigger_action(cond_action.unique_id,
                                           message=message,
                                           single_action=False,
                                           note_tags=note_tags,
                                           email_recipients=email_recipients,
                                           attachment_file=attachment_file,
                                           attachment_type=attachment_type)

    # Send email after all conditional actions have been checked
    # In order to append all action messages to send in the email
    # send_email_at_end will be None or the TO email address
    if email_recipients:
        smtp = db_retrieve_table_daemon(SMTP, entry='first')
        send_email(smtp.host, smtp.ssl, smtp.port, smtp.user, smtp.passw,
                   smtp.email_from, email_recipients, message, attachment_file,
                   attachment_type)

    # Create a note with the tags from the unique_ids in the list note_tags
    if note_tags:
        list_tags = []
        for each_note_tag_id in note_tags:
            check_tag = db_retrieve_table_daemon(NoteTags,
                                                 unique_id=each_note_tag_id)
            if check_tag:
                list_tags.append(each_note_tag_id)
        if list_tags:
            with session_scope(MYCODO_DB_PATH) as db_session:
                new_note = Notes()
                new_note.name = 'Action'
                new_note.tags = ','.join(list_tags)
                new_note.note = message
                db_session.add(new_note)

    logger_actions.debug(message)
Exemplo n.º 8
0
def trigger_function_actions(function_id, message=''):
    """
    Execute the Actions belonging to a particular Function

    :param function_id:
    :param message: The message generated from the conditional check
    :return:
    """
    logger_actions = logging.getLogger("mycodo.trigger_function_actions_{id}".format(
        id=function_id.split('-')[0]))

    # List of all email notification recipients
    # List is appended with TO email addresses when an email Action is
    # encountered. An email is sent to all recipients after all actions
    # have been executed.
    email_recipients = []

    # List of tags to add to a note
    note_tags = []

    attachment_file = None
    attachment_type = None

    actions = db_retrieve_table_daemon(Actions)
    actions = actions.filter(
        Actions.function_id == function_id).all()

    for cond_action in actions:
        (message,
         note_tags,
         email_recipients,
         attachment_file,
         attachment_type) = trigger_action(cond_action.unique_id,
                                           message=message,
                                           single_action=False,
                                           note_tags=note_tags,
                                           email_recipients=email_recipients,
                                           attachment_file=attachment_file,
                                           attachment_type=attachment_type)

    # Send email after all conditional actions have been checked
    # In order to append all action messages to send in the email
    # send_email_at_end will be None or the TO email address
    if email_recipients:
        smtp = db_retrieve_table_daemon(SMTP, entry='first')
        send_email(smtp.host, smtp.ssl, smtp.port,
                   smtp.user, smtp.passw, smtp.email_from,
                   email_recipients, message,
                   attachment_file, attachment_type)

    # Create a note with the tags from the unique_ids in the list note_tags
    if note_tags:
        list_tags = []
        for each_note_tag_id in note_tags:
            check_tag = db_retrieve_table_daemon(
                NoteTags, unique_id=each_note_tag_id)
            if check_tag:
                list_tags.append(each_note_tag_id)
        if list_tags:
            with session_scope(MYCODO_DB_PATH) as db_session:
                new_note = Notes()
                new_note.name = 'Action'
                new_note.tags = ','.join(list_tags)
                new_note.note = message
                db_session.add(new_note)

    logger_actions.debug(message)
Exemplo n.º 9
0
def trigger_action(
        cond_action_id,
        message='',
        note_tags=None,
        email_recipients=None,
        attachment_file=None,
        attachment_type=None,
        single_action=False):
    """
    Trigger individual action

    If single_action == False, message, note_tags, email_recipients,
    attachment_file, and attachment_type are returned and may be
    passed back to this function in order to append to those lists.

    :param cond_action_id: unique_id of action
    :param message: message string to append to that will be sent back
    :param note_tags: list of note tags to use if creating a note
    :param email_recipients: list of email addresses to notify if sending an email
    :param attachment_file: string location of a file to attach to an email
    :param attachment_type: string type of email attachment
    :param single_action: True if only one action is being triggered, False if only one of multiple actions
    :return: message or (message, note_tags, email_recipients, attachment_file, attachment_type)
    """
    cond_action = db_retrieve_table_daemon(Actions, unique_id=cond_action_id)
    if not cond_action:
        message += 'Error: Action with ID {} not found!'.format(cond_action_id)
        if single_action:
            return message
        else:
            return message, note_tags, email_recipients, attachment_file, attachment_type

    logger_actions = logging.getLogger("mycodo.trigger_action_{id}".format(
        id=cond_action.unique_id.split('-')[0]))

    message += "\n[Action {id}]:".format(
        id=cond_action.unique_id.split('-')[0], action_type=cond_action.action_type)

    try:
        control = DaemonControl()

        smtp_table = db_retrieve_table_daemon(
            SMTP, entry='first')
        smtp_max_count = smtp_table.hourly_max
        smtp_wait_timer = smtp_table.smtp_wait_timer
        email_count = smtp_table.email_count

        # Pause
        if cond_action.action_type == 'pause_actions':
            message += " [{id}] Pause actions for {sec} seconds.".format(
                id=cond_action.id,
                sec=cond_action.pause_duration)

            time.sleep(cond_action.pause_duration)

        # Actuate output (duration)
        if (cond_action.action_type == 'output' and
                cond_action.do_unique_id and
                cond_action.do_output_state in ['on', 'off']):
            this_output = db_retrieve_table_daemon(
                Output, unique_id=cond_action.do_unique_id, entry='first')
            message += " Turn output {unique_id} ({id}, {name}) {state}".format(
                unique_id=cond_action.do_unique_id,
                id=this_output.id,
                name=this_output.name,
                state=cond_action.do_output_state)
            if (cond_action.do_output_state == 'on' and
                    cond_action.do_output_duration):
                message += " for {sec} seconds".format(
                    sec=cond_action.do_output_duration)
            message += "."

            output_on_off = threading.Thread(
                target=control.output_on_off,
                args=(cond_action.do_unique_id,
                      cond_action.do_output_state,),
                kwargs={'duration': cond_action.do_output_duration})
            output_on_off.start()

        # Actuate output (PWM)
        if (cond_action.action_type == 'output_pwm' and
                cond_action.do_unique_id and
                0 <= cond_action.do_output_pwm <= 100):
            this_output = db_retrieve_table_daemon(
                Output, unique_id=cond_action.do_unique_id, entry='first')
            message += " Turn output {unique_id} ({id}, {name}) duty cycle to {duty_cycle}%.".format(
                unique_id=cond_action.do_unique_id,
                id=this_output.id,
                name=this_output.name,
                duty_cycle=cond_action.do_output_pwm)

            output_on = threading.Thread(
                target=control.output_on,
                args=(cond_action.do_unique_id,),
                kwargs={'duty_cycle': cond_action.do_output_pwm})
            output_on.start()

        # Execute command in shell
        if cond_action.action_type == 'command':

            # Replace string variables with actual values
            command_str = cond_action.do_action_string

            # TODO: Maybe get this working again with the new measurement system
            # # Replace measurement variables
            # if last_measurement:
            #     command_str = command_str.replace(
            #         "((measure_{var}))".format(
            #             var=device_measurement), str(last_measurement))
            # if device and device.period:
            #     command_str = command_str.replace(
            #         "((measure_period))", str(device.period))
            # if input_dev:
            #     command_str = command_str.replace(
            #         "((measure_location))", str(input_dev.location))
            # if input_dev and device_measurement == input_dev.measurements:
            #     command_str = command_str.replace(
            #         "((measure_linux_command))", str(last_measurement))
            #
            # # Replace output variables
            # if output:
            #     if output.pin:
            #         command_str = command_str.replace(
            #             "((output_pin))", str(output.pin))
            #     if output_state:
            #         command_str = command_str.replace(
            #             "((output_action))", str(output_state))
            #     if on_duration:
            #         command_str = command_str.replace(
            #             "((output_duration))", str(on_duration))
            #     if duty_cycle:
            #         command_str = command_str.replace(
            #             "((output_pwm))", str(duty_cycle))
            #
            # # Replace edge variables
            # if edge:
            #     command_str = command_str.replace(
            #         "((edge_state))", str(edge))

            message += " Execute '{com}' ".format(
                com=command_str)

            _, _, cmd_status = cmd_output(command_str)

            message += "(return status: {stat}).".format(stat=cmd_status)

        # Create Note
        if cond_action.action_type == 'create_note':
            tag_name = db_retrieve_table_daemon(
                NoteTags, unique_id=cond_action.do_action_string).name

            message += " Create note with tag '{}'.".format(tag_name)
            if single_action and cond_action.do_action_string:
                    list_tags = []
                    check_tag = db_retrieve_table_daemon(
                        NoteTags, unique_id=cond_action.do_action_string)
                    if check_tag:
                        list_tags.append(cond_action.do_action_string)

                    if list_tags:
                        with session_scope(MYCODO_DB_PATH) as db_session:
                            new_note = Notes()
                            new_note.name = 'Action'
                            new_note.tags = ','.join(list_tags)
                            new_note.note = message
                            db_session.add(new_note)
            else:
                note_tags.append(cond_action.do_action_string)

        # Capture photo
        # If emailing later, store location of photo as attachment_file
        if cond_action.action_type in ['photo', 'photo_email']:
            this_camera = db_retrieve_table_daemon(
                Camera, unique_id=cond_action.do_unique_id, entry='first')
            message += "  Capturing photo with camera {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=this_camera.id,
                name=this_camera.name)
            camera_still = db_retrieve_table_daemon(
                Camera, unique_id=cond_action.do_unique_id)
            attachment_path_file = camera_record('photo', camera_still.unique_id)
            attachment_file = os.path.join(attachment_path_file[0], attachment_path_file[1])

        # Capture video
        if cond_action.action_type in ['video', 'video_email']:
            this_camera = db_retrieve_table_daemon(
                Camera, unique_id=cond_action.do_unique_id, entry='first')
            message += "  Capturing video with camera {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=this_camera.id,
                name=this_camera.name)
            camera_stream = db_retrieve_table_daemon(
                Camera, unique_id=cond_action.do_unique_id)
            attachment_path_file = camera_record(
                'video', camera_stream.unique_id,
                duration_sec=cond_action.do_camera_duration)
            attachment_file = os.path.join(attachment_path_file[0], attachment_path_file[1])

        # Activate Controller
        if cond_action.action_type == 'activate_controller':
            (controller_type,
             controller_object,
             controller_entry) = which_controller(
                cond_action.do_unique_id)
            message += " Activate Controller {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=controller_entry.id,
                name=controller_entry.name)
            if controller_entry.is_activated:
                message += " Notice: Controller is already active!"
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_cont = new_session.query(controller_object).filter(
                        controller_object.unique_id == cond_action.do_unique_id).first()
                    mod_cont.is_activated = True
                    new_session.commit()
                activate_controller = threading.Thread(
                    target=control.controller_activate,
                    args=(controller_type,
                          cond_action.do_unique_id,))
                activate_controller.start()

        # Deactivate Controller
        if cond_action.action_type == 'deactivate_controller':
            (controller_type,
             controller_object,
             controller_entry) = which_controller(
                cond_action.do_unique_id)
            message += " Deactivate Controller {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=controller_entry.id,
                name=controller_entry.name)
            if not controller_entry.is_activated:
                message += " Notice: Controller is already inactive!"
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_cont = new_session.query(controller_object).filter(
                        controller_object.unique_id == cond_action.do_unique_id).first()
                    mod_cont.is_activated = False
                    new_session.commit()
                deactivate_controller = threading.Thread(
                    target=control.controller_deactivate,
                    args=(controller_type,
                          cond_action.do_unique_id,))
                deactivate_controller.start()

        # Resume PID controller
        if cond_action.action_type == 'resume_pid':
            pid = db_retrieve_table_daemon(
                PID, unique_id=cond_action.do_unique_id, entry='first')
            message += " Resume PID {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=pid.id,
                name=pid.name)
            if not pid.is_paused:
                message += " Notice: PID is not paused!"
            elif pid.is_activated:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.is_paused = False
                    new_session.commit()
                resume_pid = threading.Thread(
                    target=control.pid_resume,
                    args=(cond_action.do_unique_id,))
                resume_pid.start()

        # Pause PID controller
        if cond_action.action_type == 'pause_pid':
            pid = db_retrieve_table_daemon(
                PID, unique_id=cond_action.do_unique_id, entry='first')
            message += " Pause PID {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=pid.id,
                name=pid.name)
            if pid.is_paused:
                message += " Notice: PID is already paused!"
            elif pid.is_activated:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.is_paused = True
                    new_session.commit()
                pause_pid = threading.Thread(
                    target=control.pid_pause,
                    args=(cond_action.do_unique_id,))
                pause_pid.start()

        # Set PID Setpoint
        if cond_action.action_type == 'setpoint_pid':
            pid = db_retrieve_table_daemon(
                PID, unique_id=cond_action.do_unique_id, entry='first')
            message += " Set Setpoint of PID {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=pid.id,
                name=pid.name)
            if pid.is_activated:
                setpoint_pid = threading.Thread(
                    target=control.pid_set,
                    args=(pid.unique_id,
                          'setpoint',
                          float(cond_action.do_action_string),))
                setpoint_pid.start()
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.setpoint = cond_action.do_action_string
                    new_session.commit()

        # Set PID Method and start method from beginning
        if cond_action.action_type == 'method_pid':
            pid = db_retrieve_table_daemon(
                PID, unique_id=cond_action.do_unique_id, entry='first')
            message += " Set Method of PID {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=pid.id,
                name=pid.name)

            # Instruct method to start
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_pid = new_session.query(PID).filter(
                    PID.unique_id == cond_action.do_unique_id).first()
                mod_pid.method_start_time = 'Ready'
                new_session.commit()

            pid = db_retrieve_table_daemon(
                PID, unique_id=cond_action.do_unique_id, entry='first')
            if pid.is_activated:
                method_pid = threading.Thread(
                    target=control.pid_set,
                    args=(pid.unique_id,
                          'method',
                          cond_action.do_action_string,))
                method_pid.start()
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.method_id = cond_action.do_action_string
                    new_session.commit()

        # Email the Conditional message. Optionally capture a photo or
        # video and attach to the email.
        if cond_action.action_type in [
                'email', 'photo_email', 'video_email']:

            if (email_count >= smtp_max_count and
                    time.time() < smtp_wait_timer):
                allowed_to_send_notice = False
            else:
                if time.time() > smtp_wait_timer:
                    with session_scope(MYCODO_DB_PATH) as new_session:
                        mod_smtp = new_session.query(SMTP).first()
                        mod_smtp.email_count = 0
                        mod_smtp.smtp_wait_timer = time.time() + 3600
                        new_session.commit()
                allowed_to_send_notice = True

            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_smtp = new_session.query(SMTP).first()
                mod_smtp.email_count += 1
                new_session.commit()

            # If the emails per hour limit has not been exceeded
            if allowed_to_send_notice:
                message += " Notify {email}.".format(
                    email=cond_action.do_action_string)
                # attachment_type != False indicates to
                # attach a photo or video
                if cond_action.action_type == 'photo_email':
                    message += " Photo attached to email."
                    attachment_type = 'still'
                elif cond_action.action_type == 'video_email':
                    message += " Video attached to email."
                    attachment_type = 'video'

                if single_action and cond_action.do_action_string:
                    smtp = db_retrieve_table_daemon(SMTP, entry='first')
                    send_email(smtp.host, smtp.ssl, smtp.port,
                               smtp.user, smtp.passw, smtp.email_from,
                               [cond_action.do_action_string], message,
                               attachment_file, attachment_type)
                else:
                    email_recipients.append(cond_action.do_action_string)
            else:
                logger_actions.error(
                    "Wait {sec:.0f} seconds to email again.".format(
                        sec=smtp_wait_timer - time.time()))

        if cond_action.action_type == 'flash_lcd_on':
            lcd = db_retrieve_table_daemon(
                LCD, unique_id=cond_action.do_unique_id)
            message += " LCD {unique_id} ({id}, {name}) Flash On.".format(
                unique_id=cond_action.do_unique_id,
                id=lcd.id,
                name=lcd.name)

            start_flashing = threading.Thread(
                target=control.lcd_flash,
                args=(cond_action.do_unique_id, True,))
            start_flashing.start()

        if cond_action.action_type == 'flash_lcd_off':
            lcd = db_retrieve_table_daemon(
                LCD, unique_id=cond_action.do_unique_id)
            message += " LCD {unique_id} ({id}, {name}) Flash Off.".format(
                unique_id=cond_action.do_unique_id,
                id=lcd.id,
                name=lcd.name)

            start_flashing = threading.Thread(
                target=control.lcd_flash,
                args=(cond_action.do_unique_id, False,))
            start_flashing.start()

        if cond_action.action_type == 'lcd_backlight_off':
            lcd = db_retrieve_table_daemon(
                LCD, unique_id=cond_action.do_unique_id)
            message += " LCD {unique_id} ({id}, {name}) Backlight Off.".format(
                unique_id=cond_action.do_unique_id,
                id=lcd.id,
                name=lcd.name)

            start_flashing = threading.Thread(
                target=control.lcd_backlight,
                args=(cond_action.do_unique_id, False,))
            start_flashing.start()

        if cond_action.action_type == 'lcd_backlight_on':
            lcd = db_retrieve_table_daemon(
                LCD, unique_id=cond_action.do_unique_id)
            message += " LCD {unique_id} ({id}, {name}) Backlight On.".format(
                unique_id=cond_action.do_unique_id,
                id=lcd.id,
                name=lcd.name)

            start_flashing = threading.Thread(
                target=control.lcd_backlight,
                args=(cond_action.do_unique_id, True,))
            start_flashing.start()
    except Exception:
        logger_actions.exception("Error triggering action:")
        message += " Error while executing action! See Daemon log for details."

    if single_action:
        return message
    else:
        return message, note_tags, email_recipients, attachment_file, attachment_type
Exemplo n.º 10
0
def import_notes(form):
    """
    Receive a zip file containing a CSV file and note attachments
    """
    action = '{action} {controller}'.format(
        action=gettext("Import"), controller=TRANSLATIONS['note']['title'])
    error = []

    upload_folder = os.path.join(INSTALL_DIRECTORY, 'upload')
    tmp_folder = os.path.join(upload_folder, 'mycodo_notes_tmp')

    try:
        if not form.notes_import_file.data:
            error.append('No file present')
        elif form.notes_import_file.data.filename == '':
            error.append('No file name')

        if not error:
            # Save file to upload directory
            filename = secure_filename(form.notes_import_file.data.filename)
            full_path = os.path.join(tmp_folder, filename)
            assure_path_exists(upload_folder)
            assure_path_exists(tmp_folder)
            form.notes_import_file.data.save(os.path.join(
                tmp_folder, filename))

            # Unzip file
            try:
                zip_ref = zipfile.ZipFile(full_path, 'r')
                zip_ref.extractall(tmp_folder)
                zip_ref.close()
            except Exception as err:
                logger.exception(1)
                error.append("Exception while extracting zip file: "
                             "{err}".format(err=err))

        if not error:
            found_csv = False
            for each_file in os.listdir(tmp_folder):
                if each_file.endswith('_notes_exported.csv') and not found_csv:
                    found_csv = True
                    count_notes = 0
                    count_notes_skipped = 0
                    count_attach = 0
                    logger.error(each_file)

                    file_csv = os.path.join(tmp_folder, each_file)
                    path_attachments = os.path.join(tmp_folder, 'attachments')

                    with open(file_csv, 'r') as theFile:
                        reader = csv.DictReader(theFile)
                        for line in reader:
                            if not Notes.query.filter(
                                    Notes.unique_id == line['UUID']).count():
                                count_notes += 1

                                new_note = Notes()
                                new_note.unique_id = line['UUID']
                                new_note.date_time = datetime.strptime(
                                    line['Time'], '%Y-%m-%d %H:%M:%S')
                                new_note.name = line['Name']
                                new_note.note = line['Note']

                                tag_ids = []
                                tags = {}
                                for each_tag in line['Tags'].split(';'):
                                    tags[each_tag.split(',')
                                         [0]] = each_tag.split(',')[1]
                                    tag_ids.append(each_tag.split(',')[0])

                                for each_tag_id, each_tag_name in tags.items():
                                    if (not NoteTags.query.filter(
                                            NoteTags.unique_id ==
                                            each_tag_id).count()
                                            and not NoteTags.query.filter(
                                                NoteTags.name ==
                                                each_tag_name).count()):
                                        new_tag = NoteTags()
                                        new_tag.unique_id = each_tag_id
                                        new_tag.name = each_tag_name
                                        new_tag.save()

                                    elif (not NoteTags.query.filter(
                                            NoteTags.unique_id ==
                                            each_tag_id).count()
                                          and NoteTags.query.filter(
                                              NoteTags.name ==
                                              each_tag_name).count()):
                                        new_tag = NoteTags()
                                        new_tag.unique_id = each_tag_id
                                        new_tag.name = each_tag_name + str(
                                            uuid.uuid4())[:8]
                                        new_tag.save()

                                new_note.tags = ','.join(tag_ids)
                                new_note.files = line['Files']
                                new_note.save()

                                for each_file_name in line['Files'].split(','):
                                    count_attach += 1
                                    os.rename(
                                        os.path.join(path_attachments,
                                                     each_file_name),
                                        os.path.join(PATH_NOTE_ATTACHMENTS,
                                                     each_file_name))
                            else:
                                count_notes_skipped += 1

                    if (count_notes + count_attach) == 0:
                        error.append("0 imported, {notes} skipped".format(
                            notes=count_notes_skipped))
                    else:
                        flash(
                            "Imported {notes} notes and {attach} "
                            "attachments".format(notes=count_notes,
                                                 attach=count_attach),
                            "success")

            if not found_csv:
                error.append(
                    "Cannot import notes: Could not find CSV file in ZIP archive."
                )

    except Exception as err:
        error.append("Exception: {}".format(err))
    finally:
        if os.path.isdir(tmp_folder):
            shutil.rmtree(tmp_folder)  # Delete tmp directory

    flash_success_errors(error, action, url_for('routes_page.page_export'))
Exemplo n.º 11
0
    def run_action(self, message, dict_vars):
        list_tags = []
        try:
            list_tags = dict_vars["value"]["tag"]
        except:
            for each_id_set in self.tag:
                list_tags.append(each_id_set.split(",")[0])

        try:
            name = dict_vars["value"]["name"]
        except:
            name = self.name

        try:
            note = dict_vars["value"]["note"]
        except:
            note = self.note

        self.logger.debug(
            f"Tag(s): {','.join(list_tags)}, name: {name}, note: '{note}'.")

        with session_scope(MYCODO_DB_PATH) as new_session:
            list_tag_names = []
            for each_tag in list_tags:
                # First check name
                tag_check = new_session.query(NoteTags).filter(
                    NoteTags.name == each_tag).first()
                if tag_check:
                    list_tag_names.append(tag_check.name)
                else:
                    # Next check ID
                    tag_check = new_session.query(NoteTags).filter(
                        NoteTags.unique_id == each_tag).first()
                    if tag_check:
                        list_tag_names.append(tag_check.name)
                    else:
                        self.logger.error(
                            f"Tag with name or id '{each_tag}' does not exist."
                        )

            if not list_tag_names:
                msg = "No valid tags specified. Cannot create note."
                message += msg
                self.logger.error(msg)
                return message

            message += f" Create note with name '{name}', tag(s) '{','.join(list_tag_names)}'"
            if note:
                message += f", and note {note}"
            message += "."

            new_note = Notes()
            new_note.name = name
            new_note.tags = ','.join(list_tag_names)
            if note:
                new_note.note = note
            else:
                new_note.note = message
            new_session.add(new_note)

        self.logger.debug(f"Message: {message}")

        return message
Exemplo n.º 12
0
def trigger_action(cond_action_id,
                   message='',
                   note_tags=None,
                   email_recipients=None,
                   attachment_file=None,
                   attachment_type=None,
                   single_action=False,
                   debug=False):
    """
    Trigger individual action

    If single_action == False, message, note_tags, email_recipients,
    attachment_file, and attachment_type are returned and may be
    passed back to this function in order to append to those lists.

    :param cond_action_id: unique_id of action
    :param message: message string to append to that will be sent back
    :param note_tags: list of note tags to use if creating a note
    :param email_recipients: list of email addresses to notify if sending an email
    :param attachment_file: string location of a file to attach to an email
    :param attachment_type: string type of email attachment
    :param single_action: True if only one action is being triggered, False if only one of multiple actions
    :param debug: determine if logging level should be DEBUG

    :return: message or (message, note_tags, email_recipients, attachment_file, attachment_type)
    """
    cond_action = db_retrieve_table_daemon(Actions, unique_id=cond_action_id)
    if not cond_action:
        message += 'Error: Action with ID {} not found!'.format(cond_action_id)
        if single_action:
            return message
        else:
            return message, note_tags, email_recipients, attachment_file, attachment_type

    logger_actions = logging.getLogger("mycodo.trigger_action_{id}".format(
        id=cond_action.unique_id.split('-')[0]))

    if debug:
        logger_actions.setLevel(logging.DEBUG)
    else:
        logger_actions.setLevel(logging.INFO)

    message += "\n[Action {id}]:".format(
        id=cond_action.unique_id.split('-')[0],
        action_type=cond_action.action_type)

    try:
        control = DaemonControl()

        # Pause
        if cond_action.action_type == 'pause_actions':
            message += " [{id}] Pause actions for {sec} seconds.".format(
                id=cond_action.id, sec=cond_action.pause_duration)

            time.sleep(cond_action.pause_duration)

        # Infrared Send
        if cond_action.action_type == 'infrared_send':
            command = 'irsend SEND_ONCE {remote} {code}'.format(
                remote=cond_action.remote, code=cond_action.code)
            output, err, stat = cmd_output(command)

            # Send more than once
            if cond_action.send_times > 1:
                for _ in range(cond_action.send_times - 1):
                    time.sleep(0.5)
                    output, err, stat = cmd_output(command)

            message += " [{id}] Infrared Send " \
                       "code '{code}', remote '{remote}', times: {times}:" \
                       "\nOutput: {out}" \
                       "\nError: {err}" \
                       "\nStatus: {stat}'.".format(
                            id=cond_action.id,
                            code=cond_action.code,
                            remote=cond_action.remote,
                            times=cond_action.send_times,
                            out=output,
                            err=err,
                            stat=stat)

        # Actuate output (duration)
        if (cond_action.action_type == 'output' and cond_action.do_unique_id
                and cond_action.do_output_state in ['on', 'off']):
            this_output = db_retrieve_table_daemon(
                Output, unique_id=cond_action.do_unique_id, entry='first')
            message += " Turn output {unique_id} ({id}, {name}) {state}".format(
                unique_id=cond_action.do_unique_id,
                id=this_output.id,
                name=this_output.name,
                state=cond_action.do_output_state)
            if (cond_action.do_output_state == 'on'
                    and cond_action.do_output_duration):
                message += " for {sec} seconds".format(
                    sec=cond_action.do_output_duration)
            message += "."

            output_on_off = threading.Thread(
                target=control.output_on_off,
                args=(
                    cond_action.do_unique_id,
                    cond_action.do_output_state,
                ),
                kwargs={'duration': cond_action.do_output_duration})
            output_on_off.start()

        # Actuate output (PWM)
        if (cond_action.action_type == 'output_pwm'
                and cond_action.do_unique_id
                and 0 <= cond_action.do_output_pwm <= 100):
            this_output = db_retrieve_table_daemon(
                Output, unique_id=cond_action.do_unique_id, entry='first')
            message += " Turn output {unique_id} ({id}, {name}) duty cycle to {duty_cycle}%.".format(
                unique_id=cond_action.do_unique_id,
                id=this_output.id,
                name=this_output.name,
                duty_cycle=cond_action.do_output_pwm)

            output_on = threading.Thread(
                target=control.output_on,
                args=(cond_action.do_unique_id, ),
                kwargs={'duty_cycle': cond_action.do_output_pwm})
            output_on.start()

        # Execute command in shell
        if cond_action.action_type == 'command':

            # Replace string variables with actual values
            command_str = cond_action.do_action_string

            # TODO: Maybe get this working again with the new measurement system
            # # Replace measurement variables
            # if last_measurement:
            #     command_str = command_str.replace(
            #         "((measure_{var}))".format(
            #             var=device_measurement), str(last_measurement))
            # if device and device.period:
            #     command_str = command_str.replace(
            #         "((measure_period))", str(device.period))
            # if input_dev:
            #     command_str = command_str.replace(
            #         "((measure_location))", str(input_dev.location))
            # if input_dev and device_measurement == input_dev.measurements:
            #     command_str = command_str.replace(
            #         "((measure_linux_command))", str(last_measurement))
            #
            # # Replace output variables
            # if output:
            #     if output.pin:
            #         command_str = command_str.replace(
            #             "((output_pin))", str(output.pin))
            #     if output_state:
            #         command_str = command_str.replace(
            #             "((output_action))", str(output_state))
            #     if on_duration:
            #         command_str = command_str.replace(
            #             "((output_duration))", str(on_duration))
            #     if duty_cycle:
            #         command_str = command_str.replace(
            #             "((output_pwm))", str(duty_cycle))
            #
            # # Replace edge variables
            # if edge:
            #     command_str = command_str.replace(
            #         "((edge_state))", str(edge))

            message += " Execute '{com}' ".format(com=command_str)

            _, _, cmd_status = cmd_output(command_str)

            message += "(return status: {stat}).".format(stat=cmd_status)

        # Create Note
        if cond_action.action_type == 'create_note':
            tag_name = db_retrieve_table_daemon(
                NoteTags, unique_id=cond_action.do_action_string).name

            message += " Create note with tag '{}'.".format(tag_name)
            if single_action and cond_action.do_action_string:
                list_tags = []
                check_tag = db_retrieve_table_daemon(
                    NoteTags, unique_id=cond_action.do_action_string)
                if check_tag:
                    list_tags.append(cond_action.do_action_string)

                if list_tags:
                    with session_scope(MYCODO_DB_PATH) as db_session:
                        new_note = Notes()
                        new_note.name = 'Action'
                        new_note.tags = ','.join(list_tags)
                        new_note.note = message
                        db_session.add(new_note)
            else:
                note_tags.append(cond_action.do_action_string)

        # Capture photo
        # If emailing later, store location of photo as attachment_file
        if cond_action.action_type in ['photo', 'photo_email']:
            this_camera = db_retrieve_table_daemon(
                Camera, unique_id=cond_action.do_unique_id, entry='first')
            message += "  Capturing photo with camera {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=this_camera.id,
                name=this_camera.name)
            camera_still = db_retrieve_table_daemon(
                Camera, unique_id=cond_action.do_unique_id)
            attachment_path_file = camera_record('photo',
                                                 camera_still.unique_id)
            attachment_file = os.path.join(attachment_path_file[0],
                                           attachment_path_file[1])

        # Capture video
        if cond_action.action_type in ['video', 'video_email']:
            this_camera = db_retrieve_table_daemon(
                Camera, unique_id=cond_action.do_unique_id, entry='first')
            message += "  Capturing video with camera {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=this_camera.id,
                name=this_camera.name)
            camera_stream = db_retrieve_table_daemon(
                Camera, unique_id=cond_action.do_unique_id)
            attachment_path_file = camera_record(
                'video',
                camera_stream.unique_id,
                duration_sec=cond_action.do_camera_duration)
            attachment_file = os.path.join(attachment_path_file[0],
                                           attachment_path_file[1])

        # Activate Controller
        if cond_action.action_type == 'activate_controller':
            (controller_type, controller_object,
             controller_entry) = which_controller(cond_action.do_unique_id)
            message += " Activate Controller {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=controller_entry.id,
                name=controller_entry.name)
            if controller_entry.is_activated:
                message += " Notice: Controller is already active!"
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_cont = new_session.query(controller_object).filter(
                        controller_object.unique_id ==
                        cond_action.do_unique_id).first()
                    mod_cont.is_activated = True
                    new_session.commit()
                activate_controller = threading.Thread(
                    target=control.controller_activate,
                    args=(
                        controller_type,
                        cond_action.do_unique_id,
                    ))
                activate_controller.start()

        # Deactivate Controller
        if cond_action.action_type == 'deactivate_controller':
            (controller_type, controller_object,
             controller_entry) = which_controller(cond_action.do_unique_id)
            message += " Deactivate Controller {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                id=controller_entry.id,
                name=controller_entry.name)
            if not controller_entry.is_activated:
                message += " Notice: Controller is already inactive!"
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_cont = new_session.query(controller_object).filter(
                        controller_object.unique_id ==
                        cond_action.do_unique_id).first()
                    mod_cont.is_activated = False
                    new_session.commit()
                deactivate_controller = threading.Thread(
                    target=control.controller_deactivate,
                    args=(
                        controller_type,
                        cond_action.do_unique_id,
                    ))
                deactivate_controller.start()

        # Resume PID controller
        if cond_action.action_type == 'resume_pid':
            pid = db_retrieve_table_daemon(PID,
                                           unique_id=cond_action.do_unique_id,
                                           entry='first')
            message += " Resume PID {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name)
            if not pid.is_paused:
                message += " Notice: PID is not paused!"
            elif pid.is_activated:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.is_paused = False
                    new_session.commit()
                resume_pid = threading.Thread(
                    target=control.pid_resume,
                    args=(cond_action.do_unique_id, ))
                resume_pid.start()

        # Pause PID controller
        if cond_action.action_type == 'pause_pid':
            pid = db_retrieve_table_daemon(PID,
                                           unique_id=cond_action.do_unique_id,
                                           entry='first')
            message += " Pause PID {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name)
            if pid.is_paused:
                message += " Notice: PID is already paused!"
            elif pid.is_activated:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.is_paused = True
                    new_session.commit()
                pause_pid = threading.Thread(target=control.pid_pause,
                                             args=(cond_action.do_unique_id, ))
                pause_pid.start()

        # Set PID Setpoint
        if cond_action.action_type == 'setpoint_pid':
            pid = db_retrieve_table_daemon(PID,
                                           unique_id=cond_action.do_unique_id,
                                           entry='first')
            message += " Set Setpoint of PID {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name)
            if pid.is_activated:
                setpoint_pid = threading.Thread(
                    target=control.pid_set,
                    args=(
                        pid.unique_id,
                        'setpoint',
                        float(cond_action.do_action_string),
                    ))
                setpoint_pid.start()
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.setpoint = float(cond_action.do_action_string)
                    new_session.commit()

        # Raise PID Setpoint
        if cond_action.action_type == 'setpoint_pid_raise':
            pid = db_retrieve_table_daemon(PID,
                                           unique_id=cond_action.do_unique_id,
                                           entry='first')
            new_setpoint = pid.setpoint + float(cond_action.do_action_string)
            message += " Raise Setpoint of PID {unique_id} by {amt}, to {sp} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                amt=float(cond_action.do_action_string),
                sp=new_setpoint,
                id=pid.id,
                name=pid.name)
            if pid.is_activated:
                setpoint_pid = threading.Thread(target=control.pid_set,
                                                args=(
                                                    pid.unique_id,
                                                    'setpoint',
                                                    new_setpoint,
                                                ))
                setpoint_pid.start()
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.setpoint = new_setpoint
                    new_session.commit()

        # Lower PID Setpoint
        if cond_action.action_type == 'setpoint_pid_lower':
            pid = db_retrieve_table_daemon(PID,
                                           unique_id=cond_action.do_unique_id,
                                           entry='first')
            new_setpoint = pid.setpoint - float(cond_action.do_action_string)
            message += " Lower Setpoint of PID {unique_id} by {amt}, to {sp} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id,
                amt=float(cond_action.do_action_string),
                sp=new_setpoint,
                id=pid.id,
                name=pid.name)
            if pid.is_activated:
                setpoint_pid = threading.Thread(target=control.pid_set,
                                                args=(
                                                    pid.unique_id,
                                                    'setpoint',
                                                    new_setpoint,
                                                ))
                setpoint_pid.start()
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.setpoint = new_setpoint
                    new_session.commit()

        # Set PID Method and start method from beginning
        if cond_action.action_type == 'method_pid':
            pid = db_retrieve_table_daemon(PID,
                                           unique_id=cond_action.do_unique_id,
                                           entry='first')
            message += " Set Method of PID {unique_id} ({id}, {name}).".format(
                unique_id=cond_action.do_unique_id, id=pid.id, name=pid.name)

            # Instruct method to start
            with session_scope(MYCODO_DB_PATH) as new_session:
                mod_pid = new_session.query(PID).filter(
                    PID.unique_id == cond_action.do_unique_id).first()
                mod_pid.method_start_time = 'Ready'
                new_session.commit()

            pid = db_retrieve_table_daemon(PID,
                                           unique_id=cond_action.do_unique_id,
                                           entry='first')
            if pid.is_activated:
                method_pid = threading.Thread(target=control.pid_set,
                                              args=(
                                                  pid.unique_id,
                                                  'method',
                                                  cond_action.do_action_string,
                                              ))
                method_pid.start()
            else:
                with session_scope(MYCODO_DB_PATH) as new_session:
                    mod_pid = new_session.query(PID).filter(
                        PID.unique_id == cond_action.do_unique_id).first()
                    mod_pid.method_id = cond_action.do_action_string
                    new_session.commit()

        # Email the Conditional message to a single recipient.
        # Optionally capture a photo or video and attach to the email.
        if cond_action.action_type in ['email', 'photo_email', 'video_email']:

            message += " Notify {email}.".format(
                email=cond_action.do_action_string)
            # attachment_type != False indicates to
            # attach a photo or video
            if cond_action.action_type == 'photo_email':
                message += " Photo attached to email."
                attachment_type = 'still'
            elif cond_action.action_type == 'video_email':
                message += " Video attached to email."
                attachment_type = 'video'

            if single_action:
                # If the emails per hour limit has not been exceeded
                smtp_wait_timer, allowed_to_send_notice = check_allowed_to_email(
                )
                if allowed_to_send_notice and cond_action.do_action_string:
                    smtp = db_retrieve_table_daemon(SMTP, entry='first')
                    send_email(smtp.host, smtp.ssl, smtp.port, smtp.user,
                               smtp.passw, smtp.email_from,
                               [cond_action.do_action_string], message,
                               attachment_file, attachment_type)
                else:
                    logger_actions.error(
                        "Wait {sec:.0f} seconds to email again.".format(
                            sec=smtp_wait_timer - time.time()))
            else:
                email_recipients.append(cond_action.do_action_string)

            # Email the Conditional message to multiple recipients
            # Optionally capture a photo or video and attach to the email.
            if cond_action.action_type in ['email_multiple']:

                message += " Notify {email}.".format(
                    email=cond_action.do_action_string)
                # attachment_type != False indicates to
                # attach a photo or video
                if cond_action.action_type == 'photo_email':
                    message += " Photo attached to email."
                    attachment_type = 'still'
                elif cond_action.action_type == 'video_email':
                    message += " Video attached to email."
                    attachment_type = 'video'

                if single_action:
                    # If the emails per hour limit has not been exceeded
                    smtp_wait_timer, allowed_to_send_notice = check_allowed_to_email(
                    )
                    if allowed_to_send_notice and cond_action.do_action_string:
                        smtp = db_retrieve_table_daemon(SMTP, entry='first')
                        send_email(smtp.host, smtp.ssl, smtp.port, smtp.user,
                                   smtp.passw, smtp.email_from,
                                   cond_action.do_action_string.split(','),
                                   message, attachment_file, attachment_type)
                    else:
                        logger_actions.error(
                            "Wait {sec:.0f} seconds to email again.".format(
                                sec=smtp_wait_timer - time.time()))
                else:
                    email_recipients.extend(
                        cond_action.do_action_string.split(','))

        if cond_action.action_type == 'flash_lcd_on':
            lcd = db_retrieve_table_daemon(LCD,
                                           unique_id=cond_action.do_unique_id)
            message += " LCD {unique_id} ({id}, {name}) Flash On.".format(
                unique_id=cond_action.do_unique_id, id=lcd.id, name=lcd.name)

            start_flashing = threading.Thread(target=control.lcd_flash,
                                              args=(
                                                  cond_action.do_unique_id,
                                                  True,
                                              ))
            start_flashing.start()

        if cond_action.action_type == 'flash_lcd_off':
            lcd = db_retrieve_table_daemon(LCD,
                                           unique_id=cond_action.do_unique_id)
            message += " LCD {unique_id} ({id}, {name}) Flash Off.".format(
                unique_id=cond_action.do_unique_id, id=lcd.id, name=lcd.name)

            start_flashing = threading.Thread(target=control.lcd_flash,
                                              args=(
                                                  cond_action.do_unique_id,
                                                  False,
                                              ))
            start_flashing.start()

        if cond_action.action_type == 'lcd_backlight_off':
            lcd = db_retrieve_table_daemon(LCD,
                                           unique_id=cond_action.do_unique_id)
            message += " LCD {unique_id} ({id}, {name}) Backlight Off.".format(
                unique_id=cond_action.do_unique_id, id=lcd.id, name=lcd.name)

            start_flashing = threading.Thread(target=control.lcd_backlight,
                                              args=(
                                                  cond_action.do_unique_id,
                                                  False,
                                              ))
            start_flashing.start()

        if cond_action.action_type == 'lcd_backlight_on':
            lcd = db_retrieve_table_daemon(LCD,
                                           unique_id=cond_action.do_unique_id)
            message += " LCD {unique_id} ({id}, {name}) Backlight On.".format(
                unique_id=cond_action.do_unique_id, id=lcd.id, name=lcd.name)

            start_flashing = threading.Thread(target=control.lcd_backlight,
                                              args=(
                                                  cond_action.do_unique_id,
                                                  True,
                                              ))
            start_flashing.start()
    except Exception:
        logger_actions.exception("Error triggering action:")
        message += " Error while executing action! See Daemon log for details."

    logger_actions.debug("Message: {}".format(message))
    logger_actions.debug("Note Tags: {}".format(note_tags))
    logger_actions.debug("Email Recipients: {}".format(email_recipients))
    logger_actions.debug("Attachment Files: {}".format(attachment_file))
    logger_actions.debug("Attachment Type: {}".format(attachment_type))

    if single_action:
        return message
    else:
        return message, note_tags, email_recipients, attachment_file, attachment_type
Exemplo n.º 13
0
def import_notes(form):
    """
    Receive a zip file containing a CSV file and note attachments
    """
    action = '{action} {controller}'.format(
        action=gettext("Import"),
        controller=TRANSLATIONS['note']['title'])
    error = []

    upload_folder = os.path.join(INSTALL_DIRECTORY, 'upload')
    tmp_folder = os.path.join(upload_folder, 'mycodo_notes_tmp')
    full_path = None

    try:
        if not form.notes_import_file.data:
            error.append('No file present')
        elif form.notes_import_file.data.filename == '':
            error.append('No file name')

        if not error:
            # Save file to upload directory
            filename = secure_filename(
                form.notes_import_file.data.filename)
            full_path = os.path.join(tmp_folder, filename)
            assure_path_exists(upload_folder)
            assure_path_exists(tmp_folder)
            form.notes_import_file.data.save(
                os.path.join(tmp_folder, filename))

            # Unzip file
            try:
                zip_ref = zipfile.ZipFile(full_path, 'r')
                zip_ref.extractall(tmp_folder)
                zip_ref.close()
            except Exception as err:
                logger.exception(1)
                error.append("Exception while extracting zip file: "
                             "{err}".format(err=err))

        if not error:
            found_csv = False
            for each_file in os.listdir(tmp_folder):
                if each_file.endswith('_notes_exported.csv') and not found_csv:
                    found_csv = True
                    count_notes = 0
                    count_notes_skipped = 0
                    count_attach = 0
                    logger.error(each_file)

                    file_csv = os.path.join(tmp_folder, each_file)
                    path_attachments = os.path.join(tmp_folder, 'attachments')

                    with open(file_csv, 'r' ) as theFile:
                        reader = csv.DictReader(theFile)
                        for line in reader:
                            if not Notes.query.filter(Notes.unique_id == line['UUID']).count():
                                count_notes += 1

                                new_note = Notes()
                                new_note.unique_id = line['UUID']
                                new_note.date_time = datetime.strptime(line['Time'], '%Y-%m-%d %H:%M:%S')
                                new_note.name = line['Name']
                                new_note.note = line['Note']

                                tag_ids = []
                                tags = {}
                                for each_tag in line['Tags'].split(';'):
                                    tags[each_tag.split(',')[0]] = each_tag.split(',')[1]
                                    tag_ids.append(each_tag.split(',')[0])

                                for each_tag_id, each_tag_name in tags.items():
                                    if (not NoteTags.query.filter(NoteTags.unique_id == each_tag_id).count() and
                                            not NoteTags.query.filter(NoteTags.name == each_tag_name).count()):
                                        new_tag = NoteTags()
                                        new_tag.unique_id = each_tag_id
                                        new_tag.name = each_tag_name
                                        new_tag.save()

                                    elif (not NoteTags.query.filter(NoteTags.unique_id == each_tag_id).count() and
                                            NoteTags.query.filter(NoteTags.name == each_tag_name).count()):
                                        new_tag = NoteTags()
                                        new_tag.unique_id = each_tag_id
                                        new_tag.name = each_tag_name + str(uuid.uuid4())[:8]
                                        new_tag.save()

                                new_note.tags = ','.join(tag_ids)
                                new_note.files = line['Files']
                                new_note.save()

                                for each_file in line['Files'].split(','):
                                    count_attach += 1
                                    os.rename(os.path.join(path_attachments, each_file),
                                              os.path.join(PATH_NOTE_ATTACHMENTS, each_file))
                            else:
                                count_notes_skipped += 1

                    if (count_notes + count_attach) == 0:
                        error.append("0 imported, {notes} skipped".format(
                            notes=count_notes_skipped))
                    else:
                        flash("Imported {notes} notes and {attach} "
                              "attachments".format(notes=count_notes,
                                                   attach=count_attach),
                              "success")

            if not found_csv:
                error.append("Cannot import notes: Could not find CSV file in ZIP archive.")

    except Exception as err:
        error.append("Exception: {}".format(err))
    finally:
        if os.path.isdir(tmp_folder):
            shutil.rmtree(tmp_folder)  # Delete tmp directory

    flash_success_errors(error, action, url_for('routes_page.page_export'))