Beispiel #1
0
    def is_subscription_over(user):
        over = False

        try:
            subscription = Subscription.objects.get(user=user)
            if subscription.days_left == 0:
                over = True

                try:
                    action = Action.objects.get(_for=user.email, what="subscription_almost_over")
                    action.delete()
                except Action.DoesNotExist:
                    pass

            if over:
                try:
                    action = Action.objects.get(_for=user.email, what="subscription_over")
                except Action.DoesNotExist:
                    action = Action(_for=user.email, what="subscription_over", created_by=user)
                    action.save()
            else:
                try:
                    action = Action.objects.get(_for=user.email, what="subscription_over")
                    action.delete()
                except Action.DoesNotExist:
                    pass

        except Subscription.DoesNotExist:
            pass
Beispiel #2
0
def add_action(request):
    '''
    新增操作模板
    '''    
    if request.POST:
        action_type = request.POST.get('action_type', None)  # 类型。如,apache,lse
        name = request.POST.get('name', None)  # 名称。如,LSE4部署。
        run_os = request.POST.get('run_os', None)  # 操作模板正常执行所需的操作系统
        action_cmd = request.POST.get('action_cmd', None)  # 操作
        if action_cmd == None or action_cmd == '':
            if request.FILES:
                f = request.FILES['file']           
                sh = f.read()       
                try:
                    action_cmd = sh.decode('utf-8-sig')
                except:           
                    action_cmd = sh.decode('gb2312')
                f.close()
        
        action = Action.objects.filter(action_type__iexact=action_type, name__iexact=name)
        if action:
            return HttpResponse(simplejson.dumps({"statusCode": 302,
                                                  "navTabId": request.POST.get('navTabId', 'actionindex'),
                                                  "callbackType": request.POST.get('callbackType', None),
                                                  "message": u'此操作模板已存在,不能添加',
                                                  "info": u'此操作模板已存在,不能添加',
                                                  "result": u'此操作模板已存在,不能添加'}),
                                mimetype='application/json')
        action = Action(action_type=action_type, name=name,
                        run_os=run_os, action_cmd=action_cmd)
        action.save()
        Log(username=request.user.username,
            content=u"操作模板添加成功,服务名称是:" + name).save()          
        return render_to_response('action/uploadsuccess.html', {'name': action.name})
    return render_to_response('action/add_action.html', context_instance=RequestContext(request))
Beispiel #3
0
def evaluate_action(
    action: Action,
    extra_string: str = None,
    column_name: str = None,
    exclude_values: List[str] = None,
) -> List[List]:
    """Evaluate the content in an action based on the values in the columns.

    Given an action object and an optional string:
    1) Access the attached workflow
    2) Obtain the data from the appropriate data frame
    3) Loop over each data row and
      3.1) Evaluate the conditions with respect to the values in the row
      3.2) Create a context with the result of evaluating the conditions,
           attributes and column names to values
      3.3) Run the template with the context
      3.4) Run the optional string argument with the template and the context
      3.5) Select the optional column_name
    4) Return the resulting objects:
       List of (HTMLs body, extra string, column name value)
        or an error message

    :param action: Action object with pointers to conditions, filter,
                   workflow, etc.
    :param extra_string: An extra string to process (something like the email
           subject line) with the same dictionary as the text in the action.
    :param column_name: Column from where to extract the special value (
           typically the email address) and include it in the result.
    :param exclude_values: List of values in the column to exclude
    :return: list of lists resulting from the evaluation of the action
    """
    # Get the table data
    rows = get_rows(action.workflow.get_data_frame_table_name(),
                    filter_formula=action.get_filter_formula())
    list_of_renders = []
    for row in rows:
        if exclude_values and str(row[column_name]) in exclude_values:
            # Skip the row with the col_name in exclude values
            continue

        # Step 4: Create the context with the attributes, the evaluation of the
        # conditions and the values of the columns.
        context = get_action_evaluation_context(action, row)

        # Append result
        list_of_renders.append(
            render_tuple_result(action, context, extra_string, column_name), )

    # Check field n_rows_selected (table may have been modified)
    action_filter = action.get_filter()
    if action_filter and action_filter.n_rows_selected != rows.rowcount:
        # Filter now returns different number of rows. Action conditions need
        # to be refreshed
        action_filter.n_rows_selected = rows.rowcount
        action.update_n_rows_selected(filter_formula=action_filter.formula)

    return list_of_renders
Beispiel #4
0
def run_json_action(
    req: HttpRequest,
    workflow: Workflow,
    action: Action,
) -> HttpResponse:
    """Request data to send JSON objects.

    Form asking for token, key_column and if an item confirmation step is
    needed

    :param req: HTTP request (GET)
    :param workflow: workflow being processed
    :param action: Action begin run
    :return: HTTP response
    """
    # Get the payload from the session, and if not, use the given one
    action_info = get_or_set_action_info(
        req.session,
        JSONPayload,
        initial_values={
            'action_id': action.id,
            'prev_url': reverse('action:run', kwargs={'pk': action.id}),
            'post_url': reverse('action:json_done')
        },
    )

    # Create the form to ask for the email subject and other information
    form = JSONActionForm(req.POST or None,
                          column_names=[
                              col.name
                              for col in workflow.columns.filter(is_key=True)
                          ],
                          action_info=action_info)

    if req.method == 'POST' and form.is_valid():
        if action_info['confirm_items']:
            # Add information to the session object to execute the next pages
            action_info['button_label'] = ugettext('Send')
            action_info['valuerange'] = 2
            action_info['step'] = 2
            set_action_payload(req.session, action_info.get_store())

            return redirect('action:item_filter')

        # Go straight to the final step.
        return run_json_done(req, action_info=action_info, workflow=workflow)

    # Render the form
    return render(
        req, 'action/request_json_data.html', {
            'action': action,
            'num_msgs': action.get_rows_selected(),
            'form': form,
            'valuerange': range(2),
            'rows_all_false': action.get_row_all_false_count()
        })
Beispiel #5
0
def create_action(user,verb,target=None):
    # 检查最后一分钟内的相同动作
    now = timezone.now()
    last_minute = now - datetime.timedelta(seconds=60)
    # gte 大于等于     lte 小于等于
    similar_actions = Action.objects.filter(user_id=user.id,verb=verb,created__gte=last_minute)
    if target:
        target_ct = ContentType.objects.get_for_model(target)
        similar_actions = similar_actions.filter(target_ct=target_ct,target_id=target.id)
    if not similar_actions:
        action = Action(user=user,verb=verb,target=target)
        action.save()
        return True
    return False
Beispiel #6
0
def do_clone_action(
    action: Action,
    new_workflow: Workflow = None,
    new_name: str = None,
):
    """Clone an action.

    Function that given an action clones it and changes workflow and name

    :param action: Object to clone

    :param new_workflow: New workflow object to point

    :param new_name: New name

    :return: Cloned object
    """
    if new_name is None:
        new_name = action.name
    if new_workflow is None:
        new_workflow = action.workflow

    new_action = Action(
        name=new_name,
        description_text=action.description_text,
        workflow=new_workflow,
        last_executed_log=None,
        action_type=action.action_type,
        serve_enabled=action.serve_enabled,
        active_from=action.active_from,
        active_to=action.active_to,
        rows_all_false=copy.deepcopy(action.rows_all_false),
        text_content=action.text_content,
        target_url=action.target_url,
        shuffle=action.shuffle,
    )
    new_action.save()

    try:
        # Clone the column/condition pairs field.
        for acc_tuple in action.column_condition_pair.all():
            cname = acc_tuple.condition.name if acc_tuple.condition else None
            ActionColumnConditionTuple.objects.get_or_create(
                action=new_action,
                column=new_action.workflow.columns.get(
                    name=acc_tuple.column.name),
                condition=new_action.conditions.filter(name=cname).first(),
            )

        # Clone the conditions
        for condition in action.conditions.all():
            do_clone_condition(condition, new_action)

        # Update
        new_action.save()
    except Exception as exc:
        new_action.delete()
        raise exc

    return new_action
Beispiel #7
0
def get_row_values(
    action: Action,
    row_idx: Union[int, Tuple[str, str]],
) -> Dict[str, Union[str, int, float, datetime]]:
    """Get the values in a row either by index or by key.

    Given an action and a row index, obtain the appropriate row of values
    from the data frame.

    :param action: Action object
    :param row_idx: Row index to use for evaluation
    :return Dictionary with the data row
    """
    # Step 1: Get the row of data from the DB
    filter_formula = action.get_filter_formula()

    # If row_idx is an integer, get the data by index, otherwise, by key
    if isinstance(row_idx, int):
        row = get_table_row_by_index(
            action.workflow,
            filter_formula,
            row_idx,
        )
    else:

        row = dataops.sql.row_queries.get_row(
            action.workflow.get_data_frame_table_name(),
            row_idx[0],
            row_idx[1],
            column_names=action.workflow.get_column_names(),
            filter_formula=filter_formula,
        )
    return row
Beispiel #8
0
def send_json(
    user,
    action: Action,
    log_item: Log,
    action_info: JSONPayload,
):
    """Send json objects to target URL.

    Sends the json objects evaluated per row to the URL in the action

    :param user: User object that executed the action

    :param action: Action from where to take the messages

    :param token: String to include as authorisation token

    :param key_column: Key column name to use to exclude elements (if needed)

    :param exclude_values: List of values to exclude from the mailing

    :param log_item: Log object to store results

    :return: Nothing
    """
    # Evaluate the action string and obtain the list of list of JSON objects
    action_evals = evaluate_action(
        action,
        column_name=action_info['item_column'],
        exclude_values=action_info['exclude_values'],
    )

    # Create the headers to use for all requests
    headers = {
        'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Authorization': 'Bearer {0}'.format(action_info['token']),
    }

    # Create the context for the log events
    context = {
        'user': user.id,
        'action': action.id,
    }

    # Iterate over all json objects to create the strings and check for
    # correctness
    for json_string in action_evals:
        send_and_log_json(
            user,
            action,
            json.loads(json_string[0]),
            headers,
            context,
        )

        # Update data in the overall log item
    log_item.payload['objects_sent'] = len(action_evals)
    log_item.payload['filter_present'] = action.get_filter() is not None
    log_item.payload['datetime'] = str(
        datetime.datetime.now(pytz.timezone(ontask_settings.TIME_ZONE)))
    log_item.save()
Beispiel #9
0
def send_emails(
    user,
    action: Action,
    log_item: Log,
    action_info: EmailPayload,
) -> None:
    """Send action content evaluated for each row.

    Sends the emails for the given action and with the
    given subject. The subject will be evaluated also with respect to the
    rows, attributes, and conditions.

    The messages are sent in bursts with a pause in seconds as specified by the
    configuration variables EMAIL_BURST  and EMAIL_BURST_PAUSE

    :param user: User object that executed the action
    :param action: Action from where to take the messages
    :param log_item: Log object to store results
    :param action_info: Mapping key, value as defined in EmailPayload

    :return: Send the emails
    """
    # Evaluate the action string, evaluate the subject, and get the value of
    # the email column.
    action_evals = evaluate_action(
        action,
        extra_string=action_info['subject'],
        column_name=action_info['item_column'],
        exclude_values=action_info['exclude_values'])

    check_cc_lists(action_info['cc_email'], action_info['bcc_email'])

    track_col_name = ''
    if action_info['track_read']:
        track_col_name = create_track_column(action)
        # Get the log item payload to store the tracking column
        log_item.payload['track_column'] = track_col_name
        log_item.save()

    msgs = create_messages(
        user,
        action,
        action_evals,
        track_col_name,
        action_info,
    )

    deliver_msg_burst(msgs)

    # Update data in the log item
    log_item.payload['objects_sent'] = len(action_evals)
    log_item.payload['filter_present'] = action.get_filter() is not None
    log_item.payload['datetime'] = str(
        datetime.datetime.now(pytz.timezone(ontask_settings.TIME_ZONE)), )
    log_item.save()

    if action_info['send_confirmation']:
        # Confirmation message requested
        send_confirmation_message(user, action, len(msgs))
Beispiel #10
0
def actions(request, page_name=None):
    if page_name:
        c = get_common_context(request)
        c['a'] = Action.get_by_slug(page_name)
        c['articles'] = Action.objects.all()
        c['base_url'] = 'actions'
        c['base_title'] = u'Акции'
        return render_to_response('articles_base.html', c, context_instance=RequestContext(request))
    else:
        return HttpResponseRedirect('/actions/%s/' % Action.objects.all()[0].slug)
def criar_sala(request):
    jwt_token = request.data.get('token')
    case_id = request.data.get('case_id')
    try:
        jogador = make_jogador(jwt_token)
    except:
        return Response({'error': 'Usuário não identificado'},
                        status=HTTP_403_FORBIDDEN)
    try:
        if (not case_id):
            return Response({'error': 'Erro ao encontrar o caso'},
                            status=HTTP_400_BAD_REQUEST)
        caso = Caso.objects.get(id=case_id)
    except Caso.DoesNotExist:
        return Response({'error': 'Erro ao encontrar o caso'},
                        status=HTTP_400_BAD_REQUEST)

    sala = Sala()
    jogador.sala_id = sala.id
    jogador.save()
    sala.caso_id = caso.id
    update(sala)
    sala.save()
    action = Action()
    action.text = '{} entrou na sala.'.format(jogador.name)
    action.room = sala
    action.save()
    serializer = SalaSerializer(sala)
    return Response(data=serializer.data, status=HTTP_200_OK)
Beispiel #12
0
    def create(self, validated_data, **kwargs):
        """Create the action.

        :param validated_data: Validated data
        :param kwargs: Extra material
        :return: Create the action in the DB
        """
        action_obj = None
        try:
            action_type = validated_data.get('action_type')
            if not action_type:
                if validated_data['is_out']:
                    action_type = Action.personalized_text
                else:
                    action_type = Action.survey

            action_obj = Action(
                workflow=self.context['workflow'],
                name=validated_data['name'],
                description_text=validated_data['description_text'],
                action_type=action_type,
                serve_enabled=validated_data['serve_enabled'],
                active_from=validated_data['active_from'],
                active_to=validated_data['active_to'],
                text_content=validated_data.get(
                    'content',
                    validated_data.get('text_content'),  # Legacy
                ),
                target_url=validated_data.get('target_url', ''),
                shuffle=validated_data.get('shuffle', False),
            )
            action_obj.save()

            # Load the conditions pointing to the action
            condition_data = ConditionSerializer(
                data=validated_data.get('conditions', []),
                many=True,
                context={'action': action_obj})
            if condition_data.is_valid():
                condition_data.save()
            else:
                raise Exception(_('Invalid condition data'))

            # Process the fields columns (legacy) and column_condition_pairs
            self.create_column_condition_pairs(
                validated_data,
                action_obj,
                self.context['workflow'].columns.all(),
            )
        except Exception:
            if action_obj and action_obj.id:
                ActionColumnConditionTuple.objects.filter(
                    action=action_obj,
                ).delete()
                action_obj.delete()
            raise

        return action_obj
def send_solution(request):
    jwt_token = request.data.get('token')
    solution = request.data.get('solution')
    try:
        jogador = make_jogador(jwt_token)
    except:
        return Response({'error': 'Usuário não identificado'},
                        status=HTTP_403_FORBIDDEN)
    try:
        sala = Sala.objects.get(id=jogador.sala_id)
    except Sala.DoesNotExist:
        return Response({'error': 'Usuário não está em uma sala'},
                        status=HTTP_403_FORBIDDEN)
    jogador.sala_id = ''
    jogador.pista_banco = False
    jogador.pista_bar = False
    jogador.pista_penhores = False
    jogador.pista_charutaria = False
    jogador.pista_chaveiro = False
    jogador.pista_docas = False
    jogador.pista_carruagens = False
    jogador.pista_farmacia = False
    jogador.pista_hotel = False
    jogador.pista_livraria = False
    jogador.pista_museu = False
    jogador.pista_parque = False
    jogador.pista_syard = False
    jogador.pista_teatro = False
    jogador.save()
    update(sala)
    action = Action()
    action.text = '{} enviou uma solução para o caso:\n{}'.format(
        jogador.name, solution)
    action.room = sala
    action.save()
    caso = Caso.objects.get(id=sala.caso_id)
    if (sala.jogadores == 0):
        sala.delete()
    return Response({'solution': caso.solucao}, status=HTTP_200_OK)
def entrar_sala(request):
    jwt_token = request.data.get('token')
    sala_id = request.data.get('id')
    try:
        jogador = make_jogador(jwt_token)
    except:
        return Response({'error': 'Usuário não identificado'},
                        status=HTTP_403_FORBIDDEN)

    try:
        sala = Sala.objects.get(id=sala_id)
    except Sala.DoesNotExist:
        return Response({'error': 'A sala não foi encontrada'},
                        status=HTTP_400_BAD_REQUEST)
    jogador.sala_id = sala.id
    jogador.save()
    update(sala)
    action = Action()
    action.text = '{} entrou na sala.'.format(jogador.name)
    action.room = sala
    action.save()
    serializer = SalaSerializer(sala)
    return Response(data=serializer.data, status=HTTP_200_OK)
def sair_sala(request):
    jwt_token = request.data.get('token')
    try:
        jogador = make_jogador(jwt_token)
    except:
        return Response({'error': 'Usuário não identificado'},
                        status=HTTP_403_FORBIDDEN)
    try:
        sala = Sala.objects.get(id=jogador.sala_id)
    except Sala.DoesNotExist:
        return Response({'error': 'Usuário não está em uma sala'},
                        status=HTTP_403_FORBIDDEN)
    jogador.sala_id = ''
    jogador.pista_banco = False
    jogador.pista_bar = False
    jogador.pista_penhores = False
    jogador.pista_charutaria = False
    jogador.pista_chaveiro = False
    jogador.pista_docas = False
    jogador.pista_carruagens = False
    jogador.pista_farmacia = False
    jogador.pista_hotel = False
    jogador.pista_livraria = False
    jogador.pista_museu = False
    jogador.pista_parque = False
    jogador.pista_syard = False
    jogador.pista_teatro = False
    jogador.save()
    update(sala)
    action = Action()
    action.text = '{} saiu da sala.'.format(jogador.name)
    action.room = sala
    action.save()
    serializer = SalaSerializer(sala)
    if (sala.jogadores == 0):
        sala.delete()
    return Response(data=serializer.data, status=HTTP_200_OK)
Beispiel #16
0
def serve_survey_row(
    request: HttpRequest,
    action: Action,
    user_attribute_name: str,
) -> HttpResponse:
    """Serve a request for action in.

    Function that given a request, and an action IN, it performs the lookup
     and data input of values.

    :param request: HTTP request

    :param action:  Action In

    :param user_attribute_name: The column name used to check for email

    :return:
    """
    # Get the attribute value depending if the user is managing the workflow
    # User is instructor, and either owns the workflow or is allowed to access
    # it as shared
    manager = has_access(request.user, action.workflow)
    user_attribute_value = None
    if manager:
        user_attribute_value = request.GET.get('uatv')
    if not user_attribute_value:
        user_attribute_value = request.user.email

    # Get the dictionary containing column names, attributes and condition
    # valuations:
    context = get_action_evaluation_context(
        action,
        get_row_values(
            action,
            (user_attribute_name, user_attribute_value),
        ),
    )

    if not context:
        # If the data has not been found, flag
        if not manager:
            return ontask_handler404(request, None)

        messages.error(
            request,
            _('Data not found in the table'))
        return redirect(reverse('action:run', kwargs={'pk': action.id}))

    # Get the active columns attached to the action
    colcon_items = extract_survey_questions(action, request.user)

    # Bind the form with the existing data
    form = EnterActionIn(
        request.POST or None,
        tuples=colcon_items,
        context=context,
        values=[context[colcon.column.name] for colcon in colcon_items],
        show_key=manager)

    keep_processing = (
        request.method == 'POST'
        and form.is_valid()
        and not request.POST.get('lti_version'))
    if keep_processing:
        # Update the content in the DB
        row_keys, row_values  = survey_update_row_values(
            action,
            colcon_items,
            manager,
            form.cleaned_data,
            'email',
            request.user.email,
            context)

        # Log the event and update its content in the action
        log_item = Log.objects.register(
            request.user,
            Log.SURVEY_INPUT,
            action.workflow,
            {'id': action.workflow.id,
             'name': action.workflow.name,
             'action': action.name,
             'action_id': action.id,
             'new_values': json.dumps(dict(zip(row_keys, row_values)))})

        # Modify the time of execution for the action
        action.last_executed_log = log_item
        action.save()

        # If not instructor, just thank the user!
        if not manager:
            return render(request, 'thanks.html', {})

        # Back to running the action
        return redirect(reverse('action:run', kwargs={'pk': action.id}))

    return render(
        request,
        'action/run_survey_row.html',
        {
            'form': form,
            'action': action,
            'cancel_url': reverse(
                'action:run', kwargs={'pk': action.id},
            ) if manager else None,
        },
    )
def run_canvas_email_action(
    req: HttpRequest,
    workflow: Workflow,
    action: Action,
) -> HttpResponse:
    """Request data to send JSON objects.

    Form asking for subject, item column (contains ids to select unique users),
    confirm items (add extra step to drop items), export workflow and
    target_rul (if needed).

    :param req: HTTP request (GET)
    :param workflow: workflow being processed
    :param action: Action begin run
    :return: HTTP response
    """
    # Get the payload from the session, and if not, use the given one
    action_info = get_or_set_action_info(req.session,
                                         CanvasEmailPayload,
                                         initial_values={
                                             'action_id':
                                             action.id,
                                             'prev_url':
                                             reverse('action:run',
                                                     kwargs={'pk': action.id}),
                                             'post_url':
                                             reverse('action:email_done')
                                         })

    # Create the form to ask for the email subject and other information
    form = CanvasEmailActionForm(
        req.POST or None,
        column_names=[
            col.name for col in workflow.columns.filter(is_key=True)
        ],
        action=action,
        action_info=action_info)

    if req.method == 'POST' and form.is_valid():
        # Request is a POST and is valid

        if action_info['confirm_items']:
            # Create a dictionary in the session to carry over all the
            # information to execute the next pages
            action_info['button_label'] = ugettext('Send')
            action_info['valuerange'] = 2
            action_info['step'] = 2
            set_action_payload(req.session, action_info.get_store())

            return redirect('action:item_filter')

        # Go straight to the token request step
        return canvas_get_or_set_oauth_token(req, action_info['target_url'])

    # Render the form
    return render(
        req, 'action/request_canvas_email_data.html', {
            'action': action,
            'num_msgs': action.get_rows_selected(),
            'form': form,
            'valuerange': range(2),
            'rows_all_false': action.get_row_all_false_count()
        })
Beispiel #18
0
def send_confirmation_message(
    user,
    action: Action,
    nmsgs: int,
) -> None:
    """Send the confirmation message.

    :param user: Destination email
    :param action: Action being considered
    :param nmsgs: Number of messages being sent
    :return:
    """
    # Creating the context for the confirmation email
    now = datetime.datetime.now(pytz.timezone(ontask_settings.TIME_ZONE))
    cfilter = action.get_filter()
    context = {
        'user': user,
        'action': action,
        'num_messages': nmsgs,
        'email_sent_datetime': now,
        'filter_present': cfilter is not None,
        'num_rows': action.workflow.nrows,
        'num_selected': action.get_rows_selected(),
    }

    # Create template and render with context
    try:
        html_content = Template(
            str(getattr(settings,
                        'NOTIFICATION_TEMPLATE')), ).render(Context(context))
        text_content = strip_tags(html_content)
    except TemplateSyntaxError as exc:
        raise Exception(
            _('Syntax error in notification template ({0})').format(
                str(exc)), )

    # Log the event
    Log.objects.register(
        user,
        Log.ACTION_EMAIL_NOTIFY,
        action.workflow,
        {
            'user': user.id,
            'action': action.id,
            'num_messages': nmsgs,
            'email_sent_datetime': str(now),
            'filter_present': cfilter is not None,
            'num_rows': action.workflow.nrows,
            'subject': str(getattr(settings, 'NOTIFICATION_SUBJECT')),
            'body': text_content,
            'from_email': str(getattr(settings, 'NOTIFICATION_SENDER')),
            'to_email': [user.email]
        },
    )

    # Send email out
    try:
        send_mail(str(getattr(settings, 'NOTIFICATION_SUBJECT')),
                  text_content,
                  str(getattr(settings, 'NOTIFICATION_SENDER')), [user.email],
                  html_message=html_content)
    except Exception as exc:
        raise Exception(
            _('Error when sending the notification: {0}').format(str(exc)), )
    def create(self, validated_data, **kwargs):

        # Process first the used_columns field to get a sense of how many
        # columns, their type how many of them new, etc. etc.
        new_columns = []
        for citem in validated_data['used_columns']:
            cname = citem.get('name', None)
            if not cname:
                raise Exception('Incorrect column name {0}.'.format(cname))
            col = Column.objects.filter(workflow=self.context['workflow'],
                                        name=cname).first()
            if not col:
                # new column
                if citem['is_key']:
                    raise Exception('New action cannot have non-existing key '
                                    'column {0}'.format(cname))

                # Accummulate the new columns just in case we have to undo
                # the changes
                new_columns.append(citem)
                continue

            # existing column
            if col.data_type != citem.get('data_type', None) or \
                    col.is_key != citem['is_key'] or \
                    set(col.categories) != set(citem['categories']):
                # The two columns are different
                raise Exception(
                    'Imported column {0} is different from existing '
                    'one.'.format(cname))
        new_column_names = [x['name'] for x in new_columns]

        action_obj = None
        try:
            # used_columns has been verified.
            action_obj = Action(
                workflow=self.context['workflow'],
                name=validated_data['name'],
                description_text=validated_data['description_text'],
                is_out=validated_data['is_out'],
                serve_enabled=validated_data['serve_enabled'],
                active_from=validated_data['active_from'],
                active_to=validated_data['active_to'],
                content=validated_data.get('content', ''))

            action_obj.save()

            if new_columns:
                # There are some new columns that need to be created
                column_data = ColumnSerializer(data=new_columns,
                                               many=True,
                                               context=self.context)

                # And save its content
                if column_data.is_valid():
                    column_data.save()
                    workflow = self.context['workflow']
                    df = pandas_db.load_from_db(self.context['workflow'].id)
                    if df is None:
                        # If there is no data frame, there is no point on
                        # adding columns.
                        Column.objects.filter(
                            workflow=self.context['workflow'],
                            name__in=new_column_names).delete()
                        action_obj.delete()
                        raise Exception('Action cannot be imported with and '
                                        'empty data table')

                    for col in Column.objects.filter(
                            workflow=workflow, name__in=new_column_names):
                        # Add the column with the initial value
                        df = ops.data_frame_add_column(df, col, None)

                        # Update the column position
                        col.position = len(df.columns)
                        col.save()

                    # Store the df to DB
                    ops.store_dataframe_in_db(df, workflow.id)
                else:
                    raise Exception('Unable to create column data')

            # Load the conditions pointing to the action
            condition_data = ConditionSerializer(
                data=validated_data.get('conditions', []),
                many=True,
                context={'action': action_obj})
            if condition_data.is_valid():
                condition_data.save()
            else:
                raise Exception('Unable to create condition information')

            # Update the condition variables for each formula if not present
            for condition in action_obj.conditions.all():
                if condition.columns.all().count() == 0:
                    col_names = get_variables(condition.formula)
                    # Add the corresponding columns to the condition
                    condition.columns.set(
                        self.context['workflow'].columns.filter(
                            name__in=col_names))

            # Load the columns field
            columns = ColumnNameSerializer(data=validated_data['columns'],
                                           many=True,
                                           required=False,
                                           context=self.context)
            if columns.is_valid():
                for citem in columns.data:
                    column = action_obj.workflow.columns.get(
                        name=citem['name'])
                    action_obj.columns.add(column)
                columns.save()
            else:
                raise Exception('Unable to create columns field')
        except Exception:
            if action_obj and action_obj.id:
                action_obj.delete()
            Column.objects.filter(workflow=self.context['workflow'],
                                  name__in=new_column_names).delete()
            raise

        return action_obj
Beispiel #20
0
def send_canvas_emails(
    user,
    action: Action,
    log_item: Log,
    action_info: Mapping,
):
    """Send CANVAS emails with the action content evaluated for each row.

    Performs the submission of the emails for the given action and with the
    given subject. The subject will be evaluated also with respect to the
    rows, attributes, and conditions.
    :param user: User object that executed the action
    :param action: Action from where to take the messages
    :param log_item: Log object to store results
    :param action_info: Mapping key, value as defined in CanvasEmailPayload
    :return: Send the emails
    """
    # Evaluate the action string, evaluate the subject, and get the value of
    # the email column.
    action_evals = evaluate_action(
        action,
        extra_string=action_info['subject'],
        column_name=action_info['item_column'],
        exclude_values=action_info['exclude_values'])

    # Get the oauth info
    target_url = action_info['target_url']
    oauth_info = ontask_settings.CANVAS_INFO_DICT.get(target_url)
    if not oauth_info:
        raise Exception(_('Unable to find OAuth Information Record'))

    # Get the token
    user_token = OnTaskOAuthUserTokens.objects.filter(
        user=user,
        instance_name=target_url,
    ).first()
    if not user_token:
        # There is no token, execution cannot proceed
        raise Exception(_('Incorrect execution due to absence of token'))

    # Create the headers to use for all requests
    headers = {
        'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
        'Authorization': 'Bearer {0}'.format(user_token.access_token),
    }

    # Create the context for the log events
    context = {
        'user': user.id,
        'action': action.id,
    }

    # Send the objects to the given URL
    idx = 1
    burst = oauth_info['aux_params'].get('burst')
    burst_pause = oauth_info['aux_params'].get('pause', 0)
    domain = oauth_info['domain_port']
    conversation_url = oauth_info['conversation_url'].format(domain)
    for msg_body, msg_subject, msg_to in action_evals:
        #
        # JSON object to send. Taken from method.conversations.create in
        # https://canvas.instructure.com/doc/api/conversations.html
        #
        canvas_email_payload = {
            'recipients[]': msg_to,
            'body': msg_body,
            'subject': msg_subject,
        }

        # Manage the bursts
        do_burst_pause(burst, burst_pause, idx)
        # Index to detect bursts
        idx += 1

        # Send the email
        if ontask_settings.EXECUTE_ACTION_JSON_TRANSFER:
            result_msg, response_status = send_single_canvas_message(
                target_url,
                conversation_url,
                canvas_email_payload,
                headers,
                oauth_info,
            )
        else:
            # Print the JSON that would be sent through the logger
            logger.info(
                'SEND JSON({target}): {obj}',
                extra={
                    'target': target_url,
                    'obj': json.dumps(canvas_email_payload),
                },
            )
            result_msg = 'SENT TO LOGGER'
            response_status = 200

        # Log message sent
        context['object'] = json.dumps(canvas_email_payload)
        context['status'] = response_status
        context['result'] = result_msg
        context['email_sent_datetime'] = str(
            datetime.datetime.now(pytz.timezone(ontask_settings.TIME_ZONE)), )
        Log.objects.register(user, Log.ACTION_CANVAS_EMAIL_SENT,
                             action.workflow, context)

    # Update data in the overall log item
    log_item.payload['objects_sent'] = len(action_evals)
    log_item.payload['filter_present'] = action.get_filter() is not None
    log_item.payload['datetime'] = str(
        datetime.datetime.now(pytz.timezone(ontask_settings.TIME_ZONE)))
    log_item.save()

    return None
Beispiel #21
0
def save_condition_form(
    request: HttpRequest,
    form,
    template_name: str,
    action: Action,
    is_filter: Optional[bool] = False,
) -> JsonResponse:
    """
    Process the AJAX form to create and update conditions and filters.

    :param request: HTTP request

    :param form: Form being used to ask for the fields

    :param template_name: Template being used to render the form

    :param action: The action to which the condition is attached to

    :param is_filter: The condition is a filter

    :return: JSON response
    """
    if request.method == 'POST' and form.is_valid():

        if not form.has_changed():
            return JsonResponse({'html_redirect': None})

        if is_filter and form.instance.id is None and action.get_filter():
            # Should not happen. Go back to editing the action
            return JsonResponse({'html_redirect': ''})

        is_new = form.instance.id is None

        # Update fields and save the condition
        condition = form.save(commit=False)
        condition.action = action
        condition.is_filter = is_filter
        condition.save()
        condition.columns.set(
            action.workflow.columns.filter(name__in=get_variables(
                condition.formula), ))

        # If the request has the 'action_content' field, update the action
        action_content = request.POST.get('action_content')
        if action_content:
            action.set_text_content(action_content)

        propagate_changes(condition, form.changed_data, form.old_name, is_new)

        # Store the type of event to log
        if is_new:
            if is_filter:
                log_type = Log.FILTER_CREATE
            else:
                log_type = Log.CONDITION_CREATE
        else:
            if is_filter:
                log_type = Log.FILTER_UPDATE
            else:
                log_type = Log.CONDITION_UPDATE

        # Log the event
        Log.objects.register(
            request.user, log_type, action.workflow, {
                'id': condition.id,
                'name': condition.name,
                'selected_rows': condition.n_rows_selected,
                'formula': evaluate_formula(condition.formula, EVAL_TXT),
            })

        return JsonResponse({'html_redirect': ''})

    # GET request or invalid form
    return JsonResponse({
        'html_form':
        render_to_string(template_name, {
            'form': form,
            'action_id': action.id,
            'condition': form.instance
        },
                         request=request),
    })
Beispiel #22
0
def invite_users(request, company):
    """ create a new Action entry """
    try:
        c = Company.objects.get(url_name=company)
    except Company.DoesNotExist:
        return JsonError(_("Company not found"))

    # permissions
    if not has_permission(request.user, c, 'user', 'edit'):
        return JsonError(_("You have no permissions to invite users"))

    # POST data:
    # emails is a list of dictionaries:
    # {'email': '*****@*****.**', 'permission': 'cashier'}
    try:
        data = JsonParse(request.POST.get('data'))
        if not data:
            raise KeyError
    except (ValueError, AttributeError, TypeError, KeyError):
        return JsonError(_("No data in request"))

    # check POSTed emails and permissions:
    errors = []
    valid_data = []

    for d in data:
        email = str(d.get('email'))
        if not email:
            continue

        # validate permission
        permission = str(d.get('permission'))
        if permission not in g.PERMISSION_TYPES:
            # this shouldn't happen, don't even translate
            errors.append("This permission type is not valid: " + email + ": " + permission)

        # validate this email
        try:
            validate_email(email)
            valid_data.append(d)
        except ValidationError:
            errors.append(email)

        # check if this user is already a member of this company
        if Permission.objects.filter(company=c, user__email=email).exists():
            errors.append(_("User with this email is already member of the group: ") + email)

    # do nothing if there are errors
    if len(errors) > 0:
        return JsonError(errors)

    # insert all emails as Actions
    for d in valid_data:
        email = d.get('email')
        permission = d.get('permission')

        # delete any waiting actions from the same company and for the same user;
        Action.objects.filter(company=c, receiver=email, type=g.ACTION_INVITATION, status=g.ACTION_WAITING).delete()

        # create new Action entries
        action_data = {"user_email": request.user.email,
                       "user_first_last_name": str(request.user),
                       "permission": permission}

        action = Action(
            created_by=request.user,
            company=c,
            sender=request.user.email,
            receiver=email,
            type=g.ACTION_INVITATION,
            status=g.ACTION_WAITING,
            data=json.dumps(action_data)
        )
        action.save()

        # send a different invite to non-registered users and registered users
        if BlocklogicUser.objects.filter(email=email).exists():
            template_html = 'email/invitation_for_registered_users.html'
            template_text = 'email/invitation_for_registered_users.txt'
        else:
            template_html = 'email/invitation_for_non_registered_users.html'
            template_text = 'email/invitation_for_non_registered_users.txt'

        # send email
        mail_context = {
            'company_name': c.name,
            'register_url': settings.SITE_URL + reverse('web:sign_up'),
            'login_url': settings.SITE_URL + reverse('web:select_company'),
        }

        message_html = render_to_string(template_html, mail_context)
        message_text = render_to_string(template_text, mail_context)

        if settings.DEBUG:
            print "============="
            print message_text
            print "============="
            print message_html
            print "============="
        else:
            send_email(settings.EMAIL_FROM, [email], None,
                       settings.EMAIL_SUBJECT_PREFIX + " " + _("Invitation to join company on") + " " + settings.SITE_URL,
                       message_text, message_html)

    return JsonOk()
Beispiel #23
0
def update_hints(request):
    jwt_token = request.data.get('token')
    places = [
        ('banco', 'Banco'),
        ('bar', 'Bar'),
        ('penhores', 'Casa de penhores'),
        ('charutaria', 'Charutaria'),
        ('chaveiro', 'Chaveiro'),
        ('docas', 'Docas'),
        ('carruagens', 'Estação de carruagens'),
        ('farmacia', 'Farmácia'),
        ('hotel', 'Hotel'),
        ('livraria', 'Livraria'),
        ('museu', 'Museu'),
        ('parque', 'Parque'),
        ('syard', 'Scotland Yard'),
        ('teatro', 'Teatro'),
    ]
    try:
        jogador = make_jogador(jwt_token)
    except:
        return Response({'error': 'Usuário não identificado'},
                        status=HTTP_403_FORBIDDEN)
    try:
        sala = Sala.objects.get(id=jogador.sala_id)
    except Sala.DoesNotExist:
        return Response({'error': 'Usuário não está em uma sala'},
                        status=HTTP_403_FORBIDDEN)
    for place_code, place_name in places:
        hint_place = 'pista_{}'.format(place_code)
        try:
            status = request.data[hint_place]
        except:
            continue
        print(status)
        if (str(status).lower() == 'true'):
            setattr(jogador, hint_place, True)
            action = Action()
            action.text = '{} desbloqueou a pista do(a) {}.'.format(
                jogador.name, place_name)
            action.room = sala
            action.save()
        else:
            setattr(jogador, hint_place, False)
            action = Action()
            action.text = '{} bloqueou a pista do(a) {}.'.format(
                jogador.name, place_name)
            action.room = sala
            action.save()
    jogador.save()
    serializer = JogadorSerializer(jogador)
    return Response(data=serializer.data, status=HTTP_200_OK)
def edit_action_out(
    request: HttpRequest,
    workflow: Workflow,
    action: Action,
) -> HttpResponse:
    """Edit action out.

    :param request: Request object
    :param workflow: The workflow with the action
    :param action: Action
    :return: HTML response
    """
    # Create the form
    form = EditActionOutForm(request.POST or None, instance=action)

    form_filter = FilterForm(
        request.POST or None,
        instance=action.get_filter(),
        action=action
    )

    # Processing the request after receiving the text from the editor
    if request.method == 'POST' and form.is_valid() and form_filter.is_valid():
        # Get content
        text_content = form.cleaned_data.get('text_content')

        # Render the content as a template and catch potential problems.
        if text_renders_correctly(text_content, action, form):
            # Log the event
            Log.objects.register(
                request.user,
                Log.ACTION_UPDATE,
                action.workflow,
                {'id': action.id,
                 'name': action.name,
                 'workflow_id': workflow.id,
                 'workflow_name': workflow.name,
                 'content': text_content})

            # Text is good. Update the content of the action
            action.set_text_content(text_content)

            if action.action_type == Action.personalized_json:
                # Update the target_url field
                action.target_url = form.cleaned_data['target_url']

            action.save()

            if request.POST['Submit'] == 'Submit':
                return redirect(request.get_full_path())

            return redirect('action:index')

    # This is a GET request or a faulty POST request

    # Get the filter or None
    filter_condition = action.get_filter()

    # Context to render the form
    context = {
        'filter_condition': filter_condition,
        'action': action,
        'load_summernote': action.action_type == Action.personalized_text,
        'conditions': action.conditions.filter(is_filter=False),
        'other_conditions': Condition.objects.filter(
            action__workflow=workflow, is_filter=False,
        ).exclude(action=action),
        'query_builder_ops': workflow.get_query_builder_ops_as_str(),
        'attribute_names': [
            attr for attr in list(workflow.attributes.keys())
        ],
        'columns': workflow.columns.all(),
        'stat_columns': workflow.columns.filter(is_key=False),
        'selected_rows':
            filter_condition.n_rows_selected
            if filter_condition else -1,
        'has_data': action.workflow.has_table(),
        'all_false_conditions': any(
            cond.n_rows_selected == 0
            for cond in action.conditions.all()),
        'rows_all_false': action.get_row_all_false_count(),
        'total_rows': workflow.nrows,
        'form': form,
        'form_filter': form_filter,
        'vis_scripts': PlotlyHandler.get_engine_scripts(),
    }

    # Return the same form in the same page
    return render(request, 'action/edit_out.html', context=context)
Beispiel #25
0
def edit_action_in(
    request: HttpRequest,
    workflow: Workflow,
    action: Action,
) -> HttpResponse:
    """Edit an action in.

    :param request: Request object
    :param workflow: workflow
    :param action: Action
    :return: HTTP response
    """
    # All tuples (action, column, condition) to consider
    tuples = action.column_condition_pair.all()

    # Columns
    all_columns = workflow.columns

    # Conditions
    filter_condition = action.get_filter()
    all_conditions = action.conditions.filter(is_filter=False)

    # Create the context info.
    context = {
        'action': action,
        # Workflow elements
        'total_rows': workflow.nrows,
        'query_builder_ops': workflow.get_query_builder_ops_as_str(),
        'has_data': workflow.has_table(),
        'selected_rows':
            filter_condition.n_rows_selected if filter_condition else -1,
        'all_false_conditions': any(
            cond.n_rows_selected == 0 for cond in all_conditions
        ),
        # Column elements
        'key_columns': all_columns.filter(is_key=True),
        'stat_columns': all_columns.filter(is_key=False),
        'key_selected': tuples.filter(column__is_key=True).first(),
        'has_no_key': tuples.filter(column__is_key=False).exists(),
        'any_empty_description': tuples.filter(
            column__description_text='',
            column__is_key=False,
        ).exists(),
        'columns_to_insert': all_columns.exclude(
            column_condition_pair__action=action,
        ).exclude(
            is_key=True,
        ).distinct().order_by('position'),
        'column_selected_table': ColumnSelectedTable(
            tuples.filter(column__is_key=False).values(
                'id',
                'column__id',
                'column__name',
                'column__description_text',
                'condition__name',
            ),
            orderable=False,
            extra_columns=[(
                'operations',
                OperationsColumn(
                    verbose_name='',
                    template_file=ColumnSelectedTable.ops_template,
                    template_context=lambda record: {
                        'id': record['column__id'],
                        'aid': action.id}),
            )],
            condition_list=all_conditions,
        ),
        # Conditions
        'filter_condition': filter_condition,
        'conditions': all_conditions,
        'vis_scripts': PlotlyHandler.get_engine_scripts(),
        'other_conditions': Condition.objects.filter(
            action__workflow=workflow, is_filter=False,
        ).exclude(action=action),
    }

    return render(request, 'action/edit_in.html', context)