Exemplo n.º 1
0
def view(request, username):
    """
    url = employee/view/<username>

    parameters/returns:
    employee: datos del empleado
    employee_roles: objetos ProjectDepartmentEmployeeRole (bibah) con todos los roles del empleado

    template: employee_view.html
    """

    # Check that the user is logged in and it's an administrator
    logged = get_authorized_or_403(request)
    employee = get_object_or_404(Employee, user__username=username)

    # Check that the admin has permission to view that employee
    same_company_or_403(employee, logged)

    employee_roles = check_higher_roles(logged, employee)

    is_editable_role = {
        role.id: is_role_updatable_by_user(logged, role.id)
        for role in employee_roles
    }

    return render(
        request, 'employee/employee_view.html', {
            'employee': employee,
            'employee_roles': employee_roles,
            'is_editable_role': is_editable_role
        })
Exemplo n.º 2
0
def recover(request, username):
    """
    url = employee/recover/<username>

    parameters/returns:
    Nada, redirecciona a la vista de listado de empleados

    template: ninguna
    """

    admin = get_admin_executive_or_403(request)
    employee = get_object_or_404(Employee,
                                 user__username=username,
                                 user__is_active=False)

    # Check that the admin has permission to view that employee
    same_company_or_403(admin, employee)

    employee_user = employee.user
    employee_user.is_active = True
    employee_user.save()

    EmployeeLog.objects.create(employee_id=employee,
                               event="A",
                               price_per_hour=employee.price_per_hour)
    return HttpResponseRedirect('/employee/list')
def list_project_department(request):
    """
    parameters: project_id or department_id, both optional.
    If it receives project_id, it will return all projectDepartments from that project
    If it receives department_id, it will return all projectDepartments form that department
    If it receives none, it will return all projectDeparments from the logged company

    returns:
    projectDepartments: lista de relaciones proyecto-departamento de la compañía logeada, según el parámetro pasado.

    template:
    projectdepartment_list.html
    """

    admin = get_current_admin_or_403(request)
    project_id = request.GET.get('project_id')
    department_id = request.GET.get('department_id')

    if project_id is not None:
        project = get_object_or_404(Project, id=project_id)
        same_company_or_403(admin,project)
        lista = ProjectDepartment.objects.filter(project_id=project)

    elif department_id is not None:
        department = get_object_or_404(Department, id=department_id)
        same_company_or_403(admin,department)
        lista = ProjectDepartment.objects.filter(department_id=department)

    else:
        lista = ProjectDepartment.objects.filter(project_id__company_id=admin.company_id)

    return render(request, "projectDepartment/projectdepartment_list.html", {"projectDepartments": lista})
Exemplo n.º 4
0
def ajax_tasks_per_department(request):
    """
    # Devuelve un objeto {'names': [dpto1, dpto2...], 'values': [tareas1, tareas2...]}

    # Parámetros obligatorios:
    # project_id - ID del proyecto
    """
    if "project_id" not in request.GET:
        raise SuspiciousOperation

    project_id = request.GET["project_id"]
    project = get_object_or_404(Project, pk=project_id)
    logged = check_project(request, project)
    same_company_or_403(logged, project)

    company_departments = Department.objects.filter(
        active=True, company_id=logged.company_id)

    data = {'names': [], 'values': []}

    for dpmt in company_departments:
        data['names'].append(dpmt.name)
        data['values'].append(
            Task.objects.filter(
                projectDepartment_id__project_id=project,
                projectDepartment_id__department_id=dpmt).count())

    return JsonResponse(data)
Exemplo n.º 5
0
def ajax_employees_per_task(request):
    """
    # Devuelve un objeto {'names': [tarea1, tarea2...], 'values': [tareas1, tareas2...]}

    # Parámetros obligatorios:
    # department_id - ID del departamento
    """
    if "department_id" not in request.GET:
        raise SuspiciousOperation

    department_id = request.GET["department_id"]

    department = get_object_or_404(Department, pk=department_id)
    actor = check_department(department, request)
    same_company_or_403(actor, department)

    dpmt_tasks = Task.objects.filter(
        active=True, projectDepartment_id__department_id__id=department_id)

    data = {'names': [], 'values': []}

    for task in dpmt_tasks:
        data['names'].append(task.name)
        data['values'].append(
            TimeLog.objects.filter(
                task_id=task).distinct('employee_id').count())

    return JsonResponse(data)
Exemplo n.º 6
0
def find_tuple(project, department, actor):
    """
    Returns a tuple project-department
    """
    same_company_or_403(actor,project)
    same_company_or_403(actor,department)

    return ProjectDepartment.objects.filter(project_id=project, department_id=department).first()
Exemplo n.º 7
0
def edit(request, task_id):
    """
    parameters/returns:
    form: el formulario con los datos de la tarea
    departments:eso
    projects:eso

    errors: ver create
    repeated_name: si el nombre es repetido
    invalid_goal:si el objetivo es incorrecto(está uno en blanco y otro no)
    project_department_not_related: si no están relacionados projectdepartment
    invalid_price: si el precio no esta puesto acorde al objetivo de producción (o no es positivo)

    template:
    task_form.html
    """
    # Check that the user is logged in
    task = get_object_or_404(Task, pk=task_id)
    actor=check_task(request,task)
    same_company_or_403(actor,task.actor_id)

    errors = []

    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = TaskForm(request.POST)
        # check whether it's valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required
            errors=process_task_form(form)
            # find tasks with the same name
            pro = Task.objects.filter(name=form.cleaned_data['name'],
                                      projectDepartment_id=task.projectDepartment_id).first()

            # pro does not exists or it's the same
            if pro is not None and pro.id != task.id:
                errors.append('task_creation_repeated_name')

            if not errors:
                update_task(task, form, actor)
                return HttpResponseRedirect('/task/list')

    # if a GET (or any other method) we'll create a blank form
    else:
        form = TaskForm(initial={"name": task.name, "description": task.description,
                                 "task_id": task.id,
                                 "production_goal": task.production_goal if task.production_goal is not None else "",
                                 "goal_description": task.goal_description if task.goal_description is not None else "",
                                 "project_id": task.projectDepartment_id.project_id,
                                 "department_id": task.projectDepartment_id.department_id,
                                 "price_per_unit": task.price_per_unit if task.price_per_unit is not None else "",
                                 "price_per_hour": task.price_per_hour if task.price_per_hour is not None else ""})
    # The project

    return render(request, 'task/task_form.html', {'form': form, "errors": errors,
                                              "departments": [task.projectDepartment_id.department_id], "projects": [task.projectDepartment_id.project_id]})
def create_project_department(form, admin):
    """ Creates a link between a project and a department so that tasks can be created for them"""
    project = form.cleaned_data['project_id']
    department = form.cleaned_data['department_id']

    same_company_or_403(admin,project) 
    same_company_or_403(admin,department)

    return ProjectDepartment.objects.create(project_id=project, department_id=department)
Exemplo n.º 9
0
def ajax_productivity_per_task(request, username):
    """
    # url = employee/ajax_productivity_per_task/<username>
    # Devuelve un objeto cuyas claves son las ID de los proyectos y sus valores un objeto
    #{'name': ..., 'total_productivity': X,'expected_productivity':Y} (X e Y en unidades goal_description/hora)

    #Ejemplo:
    #/employee/ajax_productivity_per_task/JoseGavilan

    #devuelve lo siguiente
    #{"3": {"total_productivity": 0.7125, "expected_productivity": 2.0, "name": "Hacer cosas de front"}}
    """
    # Check that the user is logged in and it's an administrator or with permissions

    logged = get_authorized_or_403(request)

    # Check that it's at least PM
    if get_highest_role_tier(logged) < 40:
        raise PermissionDenied

    employee = get_object_or_404(Employee,
                                 user__username=username,
                                 user__is_active=True)

    # Check that the admin has permission to view that employee
    same_company_or_403(logged, employee)

    # Find tasks with timelog in date range and annotate the sum of the production and time
    tasks = Task.objects.filter(
        active=True,
        projectDepartment_id__projectdepartmentemployeerole__employee_id=
        employee,
        production_goal__isnull=False).distinct().annotate(
            total_produced_units=Sum("timelog__produced_units"),
            total_duration=Sum("timelog__duration"))
    data = {}
    # Save productivity for each task
    for task in tasks:

        total_produced_units = task.total_produced_units
        total_duration = task.total_duration
        if total_duration is None or total_produced_units is None or total_duration == 0:
            total_productivity = 0
        else:
            # Duration is in minutes,so we multiply by 60 (duration is in the denominator)
            total_productivity = 60 * total_produced_units / total_duration

        data[task.id] = {
            'name': task.name,
            'expected_productivity': task.production_goal,
            'total_productivity': total_productivity
        }

    return JsonResponse(data)
Exemplo n.º 10
0
def edit(request, department_id):
    """
    url = department/edit/(department_id)/

    parameters/returns:
    form: el formulario con los datos del departamento
    repeated_name:true si el nombre ya existe para otro departamento

    template:
    department_form.html
    """

    # Check that the current user is an administrator or executive
    department = get_object_or_404(Department, pk=department_id)
    admin = get_admin_executive_or_403(request)
    same_company_or_403(admin, department)

    repeated_name = False

    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = DepartmentForm(request.POST)
        # check whether it's valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required
            # ...
            # redirect to a new URL:

            dep = find_name(form.cleaned_data['name'], admin)
            # dep does not exists or it's the same
            if dep is None or dep.id == department.id:
                edit_department(department, form)
                return HttpResponseRedirect('/department/view/' +
                                            str(department.id) + "/")
            else:
                repeated_name = True
    # if a GET (or any other method) we'll create a blank form
    else:
        department = get_object_or_404(Department, pk=department_id)
        form = DepartmentForm(initial={
            "name": department.name,
            "department_id": department.id
        })

    return render(request, 'department/department_form.html', {
        'form': form,
        'repeated_name': repeated_name
    })
Exemplo n.º 11
0
def edit(request, project_id):
    """
    parameters/returns:
    form: el formulario con los datos del proyecto

    template:
    project_form.html
    """
    # Check that the user is logged in
    admin = get_admin_executive_or_403(request)
    project = get_object_or_404(Project, pk=project_id)
    same_company_or_403(admin, project)

    repeated_name = False
    error = False
    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = ProjectForm(request.POST)
        # check whether it's valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required
            pro = find_name(form.cleaned_data['name'], admin)
            # pro does not exists or it's the same
            if pro is None or pro.id == project.id:
                update_project(project, form)
                return redirect('project_list')
            else:
                repeated_name = True
        else:
            error = True

    # if a GET (or any other method) we'll create a blank form
    else:
        #Check is same company
        form = ProjectForm(initial={
            "name": project.name,
            "project_id": project.id
        })

    return render(request, 'project/project_form.html', {
        'form': form,
        'repeated_name': repeated_name,
        'error': error
    })
Exemplo n.º 12
0
def delete(request, project_id):
    """
    parameters:
    project_id: the project id to delete

    returns:
    nothing

    template:
    project_list.html
    """
    # Check that the user is logged in
    admin = get_admin_executive_or_403(request)
    project = get_object_or_404(Project, pk=project_id, deleted=False)
    same_company_or_403(admin, project)
    delete_project(project)

    return HttpResponseRedirect('/project/list')
Exemplo n.º 13
0
def check_metrics_authorized_for_employee_in_project(user, employee_id,
                                                     project_id):
    """
    Raises 403 if the current actor is not allowed to obtain metrics for the department

    """
    if not user.is_authenticated():
        raise PermissionDenied

    employee = get_object_or_404(Employee, id=employee_id)
    project = get_object_or_404(Project, id=project_id)
    logged = user.actor

    # Check that the admin has permission to view that employee
    same_company_or_403(logged, employee)

    # Check that the admin has permission to view that employee
    same_company_or_403(logged, project)
Exemplo n.º 14
0
def recover(request, task_id):
    """
    parameters:
    task_id: the task id to recover

    returns:
    nothing

    template:
    task_list.html
    """
    # Check that the user is logged in
    task = get_object_or_404(Task, pk=task_id, active=False)
    actor=check_task(request,task)
    same_company_or_403(actor,task.actor_id)

    recover_task(task)

    return HttpResponseRedirect('/task/list')
Exemplo n.º 15
0
def show(request, project_id):
    """
    parameters:
    project_id

    returns:
    -project
    -project_managers
    -employees
    -tasks
    -departments
    all related to this project

    template:
    project_form.html

    """

    project = get_object_or_404(Project, pk=project_id)
    admin = check_project(request, project)
    same_company_or_403(admin, project)

    project_managers = Employee.objects.filter(
        projectdepartmentemployeerole__projectDepartment_id__project_id=project,
        projectdepartmentemployeerole__role_id__tier=40).distinct().order_by(
            "user__first_name", "user__last_name")

    employees = Employee.objects.filter(
        user__is_active=True,
        projectdepartmentemployeerole__projectDepartment_id__project_id=project,
        projectdepartmentemployeerole__role_id__tier__lte=40).distinct(
        ).order_by("user__first_name", "user__last_name")

    departments = Department.objects.filter(
        active=True, projectdepartment__project_id__id=project_id).order_by(
            "name").distinct()
    return render(
        request, "project/project_view.html", {
            "project": project,
            "employees": employees,
            "departments": departments,
            "project_managers": project_managers
        })
Exemplo n.º 16
0
def recover(request, department_id):
    """
    url = department/recover/(department_id)/
    parameters:
    department_id: the department id to recover

    returns:
    nothing

    template:
    deparment_list.html
    """
    department = get_object_or_404(Department, pk=department_id, active=False)
    # Check that the current user is an administrator or executive
    admin = get_admin_executive_or_403(request)
    same_company_or_403(admin, department)

    recover_department(department)

    return HttpResponseRedirect('/department/list')
Exemplo n.º 17
0
def view(request, task_id):
    """
    parameters:
    task_id: the task id to delete

    returns:
    task:the task
    goal_evolution: the production goal evolution for this task
    productivity: the evolution of productivity for this task

    template:
    task_view.html
    """

    task = get_object_or_404(Task, pk=task_id)
    actor=check_task(request,task, for_view=True)
    same_company_or_403(actor,task.actor_id)

    goal_evolution = GoalEvolution.objects.filter(task_id=task.id)
    #employees = Employee.objects.filter(projectdepartmentemployeerole__projectDepartment_id__task=task.id,
    #    projectdepartmentemployeerole__role_id__tier__lte=20).distinct()
    employees = Employee.objects.filter(timelog__task_id=task.id).distinct()
    return render(request, "task/task_view.html", {"task": task, "goal_evolution": goal_evolution, "employees": employees})
Exemplo n.º 18
0
def view(request, department_id):
    """
    url = department/view/(department_id)/

    parameters:
    department_id: id del department

    returns:
    department: datos del departamento
    employees: objetos Empleados
    tasks: lista de tareas del departamento

    template: department_view.html
    """
    department = get_object_or_404(Department, pk=department_id)
    actor = check_department(department, request)
    same_company_or_403(actor, department)

    coordinators = get_coordinator(department)
    # DO NOT ORDER the tasks, otherwise, by some random reason, it wont filter properly
    tasks = task_list(request).filter(
        projectDepartment_id__department_id=department).distinct()

    employees = Employee.objects.filter(
        user__is_active=True,
        projectdepartmentemployeerole__projectDepartment_id__department_id=
        department,
        projectdepartmentemployeerole__role_id__tier__lte=40).distinct(
        ).order_by("user__first_name", "user__last_name")

    return render(
        request, 'department/department_view.html', {
            'department': department,
            'employees': employees,
            'tasks': tasks,
            'coordinators': coordinators
        })
Exemplo n.º 19
0
def ajax_productivity_per_task_and_date(request, username):
    """
    # url = employee/ajax_productivity_per_task/<username>
    # Devuelve un objeto con las fechas y las productividades de la tarea real y esperada
    #{'name': ..., 'total_productivity': X,'expected_productivity':Y} (X en unidades goal_description/hora)

    # Parámetro obligatorio:
    # task_id: el id de la tarea en cuestión

    # Parámetros opcionales:
    # start_date - fecha en formato YYYY-MM-DD que indica el inicio de la medición. Por defecto, 30 días antes de la fecha actual.
    # end_date - fecha en formato YYYY-MM-DD que indica el final de la medición. Por defecto, fecha actual.
    # offset - desplazamiento (huso) horario en formato +/-HH:MM - Por defecto +00:00

    # Si se proporcionan pero no tienen el formato correcto se lanzará un error HTTP 400 Bad Request

    #Ejemplo
    #/employee/ajax_productivity_per_task_and_date/JoseGavilan?task_id=3&start_date=2017-02-05&end_date=2017-02-16

    #devuelve lo siguiente
    #{"dates":
    #   ["2017-02-05", "2017-02-06", "2017-02-07", "2017-02-08", "2017-02-09", "2017-02-10", "2017-02-11", "2017-02-12", "2017-02-13", "2017-02-14", "2017-02-15", "2017-02-16"],
    #"task": {"name": "Hacer cosas de front",
    #   "real_productivity": [0, 0, 0, 0, 0, 0, 0, 1.2, 0, 0.225, 0, 0],
    #   "task_id": 3,
    #   "expected_productivity": [9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 4.0, 4.0, 2.0, 2.0, 2.0]}}
    """

    # Get and parse the dates
    start_date = request.GET.get("start_date",
                                 str(date.today() - timedelta(days=30)))
    end_date = request.GET.get("end_date", str(date.today()))
    date_regex = re.compile("^\d{4}-\d{2}-\d{2}$")

    if date_regex.match(start_date) is None or date_regex.match(
            end_date) is None:
        raise SuspiciousOperation("Start/end date are not valid")

    offset = request.GET.get("offset", "+00:00")
    offset_regex = re.compile("^(\+|-)\d{2}:\d{2}$")

    if offset_regex.match(offset) is None:
        raise SuspiciousOperation("Time offset is not valid")

    # Append time offsets
    start_date += " 00:00" + offset
    end_date += " 00:00" + offset

    # Check that the user is logged in and it's an administrator or with permissions
    logged = get_authorized_or_403(request)

    # Check that it's at least PM
    if get_highest_role_tier(logged) < 40:
        raise PermissionDenied

    employee = get_object_or_404(Employee,
                                 user__username=username,
                                 user__is_active=True)

    # Check that the admin has permission to view that employee
    same_company_or_403(logged, employee)

    task_id = request.GET.get("task_id")
    # Find task with id requested
    task = Task.objects.filter(
        pk=task_id,
        active=True,
        projectDepartment_id__projectdepartmentemployeerole__employee_id=
        employee,
        production_goal__isnull=False).distinct().first()
    if task is None:
        raise SuspiciousOperation("The task could not be found")

    # Get all dates between start and end
    dates = []
    str_dates = []
    d1 = datetime.strptime(start_date[0:19] + start_date[20:22],
                           '%Y-%m-%d %H:%M%z')
    d2 = datetime.strptime(end_date[0:19] + end_date[20:22],
                           '%Y-%m-%d %H:%M%z')
    delta = d2 - d1  # timedelta

    for i in range(delta.days + 1):
        str_dates.append((d1 + timedelta(days=i)).date().strftime("%Y-%m-%d"))
        dates.append(d1 + timedelta(days=i))

    data = {
        'dates': str_dates,
        'task': {
            'task_id': task.id,
            'name': task.name,
            'real_productivity': [],
            'expected_productivity': []
        }
    }

    # Save productivity for each  date
    # for each date, we will find the asociated timelog
    for log_date in dates:
        log = TimeLog.objects.filter(task_id=task.id,
                                     workDate__year=log_date.year,
                                     workDate__month=log_date.month,
                                     workDate__day=log_date.day,
                                     employee_id=employee).first()
        if log is None:
            # He did not work that day
            total_productivity = 0
            total_duration = 0
        else:
            total_produced_units = log.produced_units
            total_duration = log.duration
            if total_duration == 0:
                total_productivity = 0
            else:
                # Duration is in minutes, so we multiply by 60 (duration is in the denominator)
                total_productivity = 60 * total_produced_units / total_duration

        # Find the registry date of production goal evolution which is closest to the date
        expected_productivity = GoalEvolution.objects.filter(
            task_id_id=task.id, registryDate__gte=log_date).first()

        # If we do not find the goal or if the date is after the last task update, it may be the current task goal
        if total_duration == 0:
            expected_productivity = 0
        else:
            if expected_productivity is None or task.registryDate <= log_date:
                expected_productivity = task.production_goal
            else:
                expected_productivity = expected_productivity.production_goal

        data["task"]["real_productivity"].append(
            default_round(total_productivity))
        data["task"]["expected_productivity"].append(
            default_round(expected_productivity))

    return JsonResponse(data)
Exemplo n.º 20
0
def ajax_profit_per_date(request, employee_id):
    """
    # url = employee/ajax_profit_per_date/<employee_id>
    # Devuelve un objeto con las fechas y las rentabilidades diarias y acumuladas
    #

    # Parámetro obligatorio:
    ninguno

    # Parámetros opcionales:
    # start_date - fecha en formato YYYY-MM-DD que indica el inicio de la medición. Por defecto, 30 días antes de la fecha actual.
    # end_date - fecha en formato YYYY-MM-DD que indica el final de la medición. Por defecto, fecha actual.
    # offset - desplazamiento (huso) horario en formato +/-HH:MM - Por defecto +00:00

    # Si se proporcionan pero no tienen el formato correcto se lanzará un error HTTP 400 Bad Request

    #Ejemplo
    #/employee/ajaxAcumProfit/1/

    #devuelve lo siguiente
    #{"acumExpenses": [0, 1457.18015695298, 3071.32603956358, 4438.9463044226895, 6465.819587171869, 7912.658013249849, 9791.46399488711, 11615.32872003681, 13494.726436052111, 15102.72092592163, 16718.442225021892, 18327.93613617256, 20841.87940297534, 22953.949544558982, 24314.625169466122, 25683.231076691303, 27287.16055422502, 28760.84364198999, 31104.25163724206, 32808.89759982555, 34747.27999087272, 36150.9847742294, 37523.6098087571, 38600.05927001698, 40953.76583717958, 42469.88703139726, 44081.49130458021, 45420.3135021882, 47945.57927018715, 49368.262834629466, 51133.932803674485],
    "acumIncome": [0, 155861.848663544, 262457.90948135697, 396454.85575838294, 572637.4741922909, 703418.0032829699, 889130.2419483919, 1057821.248373874, 1259349.275922576, 1393310.956579081, 1539441.608896949, 1700420.3827038072, 1955067.034572835, 2187486.6539142523, 2300530.309442004, 2429378.038836404, 2615789.2939997134, 2742614.2371285204, 3004214.3219032744, 3205025.4834073624, 3363963.7766520614, 3552325.908039063, 3718850.184141958, 3833661.86021891, 4044009.6991582112, 4159278.365569177, 4285423.634163346, 4417334.086840815, 4692230.750316469, 4819759.243153938, 4997733.5628708275],
    "dates": ["2017-03-21", "2017-03-22", "2017-03-23", "2017-03-24", "2017-03-25", "2017-03-26", "2017-03-27", "2017-03-28", "2017-03-29", "2017-03-30", "2017-03-31", "2017-04-01", "2017-04-02", "2017-04-03", "2017-04-04", "2017-04-05", "2017-04-06", "2017-04-07", "2017-04-08", "2017-04-09", "2017-04-10", "2017-04-11", "2017-04-12", "2017-04-13", "2017-04-14", "2017-04-15", "2017-04-16", "2017-04-17", "2017-04-18", "2017-04-19", "2017-04-20"],
    "income": [0, 155861.848663544, 106596.060817813, 133996.946277026, 176182.618433908, 130780.529090679, 185712.238665422, 168691.006425482, 201528.027548702, 133961.680656505, 146130.652317868, 160978.773806858, 254646.651869028, 232419.619341417, 113043.655527752, 128847.7293944, 186411.255163309, 126824.943128807, 261600.084774754, 200811.161504088, 158938.293244699, 188362.131387002, 166524.276102895, 114811.676076952, 210347.838939301, 115268.666410966, 126145.268594169, 131910.452677469, 274896.663475654, 127528.492837469, 177974.319716889],
    "expenses": [0, 1457.18015695298, 1614.1458826106, 1367.62026485911, 2026.87328274918, 1446.83842607798, 1878.80598163726, 1823.8647251497, 1879.3977160153, 1607.99448986952, 1615.72129910026, 1609.49391115067, 2513.94326680278, 2112.07014158364, 1360.67562490714, 1368.60590722518, 1603.92947753372, 1473.68308776497, 2343.40799525207, 1704.64596258349, 1938.38239104717, 1403.70478335668, 1372.6250345277, 1076.44946125988, 2353.7065671626, 1516.12119421768, 1611.60427318295, 1338.82219760799, 2525.26576799895, 1422.68356444232, 1765.66996904502]}  "expected_productivity": [9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 9.0, 4.0, 4.0, 2.0, 2.0, 2.0]}}
    """
    logged = get_authorized_or_403(request)

    # Check that it's at least PM
    if get_highest_role_tier(logged) < 40:
        raise PermissionDenied

    employee = get_object_or_404(Employee, pk=employee_id)

    # Check that the admin has permission to view that employee
    same_company_or_403(employee, logged)

    check_higher_roles(logged, employee)

    # Get and parse the dates
    start_date = request.GET.get("start_date",
                                 str(date.today() - timedelta(days=30)))
    end_date = request.GET.get("end_date", str(date.today()))
    date_regex = re.compile("^\d{4}-\d{2}-\d{2}$")

    if date_regex.match(start_date) is None or date_regex.match(
            end_date) is None:
        raise SuspiciousOperation("Start/end date are not valid")

    offset = request.GET.get("offset", "+00:00")
    offset_regex = re.compile("^(\+|-)\d{2}:\d{2}$")

    if offset_regex.match(offset) is None:
        raise SuspiciousOperation("Time offset is not valid")

    # Append time offsets
    start_date += " 00:00" + offset
    end_date += " 00:00" + offset

    # Get all dates between start and end
    dates = []
    str_dates = []

    d1 = datetime.strptime(start_date[0:19] + start_date[20:22],
                           '%Y-%m-%d %H:%M%z')
    d2 = datetime.strptime(end_date[0:19] + end_date[20:22],
                           '%Y-%m-%d %H:%M%z')
    delta = d2 - d1  # timedelta
    for i in range(delta.days + 1):
        str_dates.append((d1 + timedelta(days=i)).date().strftime("%Y-%m-%d"))
        dates.append(d1 + timedelta(days=i))

    data = {
        'dates': str_dates,
        'expenses': [],
        'income': [],
        'acumExpenses': [],
        'acumIncome': []
    }

    # Profit
    # for each date, we will find all logs, calculate the sum and acumulate it
    index = 0
    for log_date in dates:
        logs = TimeLog.objects.filter(employee_id=employee_id,
                                      workDate__year=log_date.year,
                                      workDate__month=log_date.month,
                                      workDate__day=log_date.day).distinct()
        expenses = logs.aggregate(
            total_expenses=Sum(F("duration") / 60.0 *
                               F("employee_id__price_per_hour"),
                               output_field=FloatField()))["total_expenses"]
        expenses = expenses if expenses is not None else 0
        income = logs.aggregate(total_income=Sum(
            F("task_id__price_per_unit") *
            F("produced_units")))["total_income"]
        income = income if income is not None else 0

        data['expenses'].append(default_round(expenses))
        data['income'].append(default_round(income))
        if index == 0:
            data['acumExpenses'].append(default_round(expenses))
            data['acumIncome'].append(default_round(income))
        else:
            data['acumExpenses'].append(
                default_round(data['acumExpenses'][index - 1] + expenses))
            data['acumIncome'].append(
                default_round(data['acumIncome'][index - 1] + income))
        index += 1
    return JsonResponse(data)
Exemplo n.º 21
0
def ajax_time_per_department(request):
    """
    # Devuelve un objeto {'names': [dpto1, dpto2...], 'values': [tiempo1, tiempo2...]}

    # Parámetros obligatorios:
    # project_id - ID del proyecto

    # Parámetros opcionales:
    # start_date - fecha en formato YYYY-MM-DD que indica el inicio de la medición. Por defecto, 30 días antes de la fecha actual.
    # end_date - fecha en formato YYYY-MM-DD que indica el final de la medición. Por defecto, fecha actual.
    # offset - desplazamiento (huso) horario en formato +/-HH:MM - Por defecto +00:00

    # Si se proporcionan pero no tienen el formato correcto se lanzará un error HTTP 400 Bad Request

    """
    if "project_id" not in request.GET:
        raise SuspiciousOperation

    project_id = request.GET["project_id"]
    project = get_object_or_404(Project, pk=project_id)
    logged = check_project(request, project)
    same_company_or_403(logged, project)

    # Get and parse the dates
    start_date = request.GET.get("start_date",
                                 str(date.today() - timedelta(days=30)))
    end_date = request.GET.get("end_date", str(date.today()))
    date_regex = re.compile("^\d{4}-\d{2}-\d{2}$")

    if date_regex.match(start_date) is None or date_regex.match(
            end_date) is None:
        raise SuspiciousOperation("Start/end date are not valid")

    offset = request.GET.get("offset", "+00:00")
    offset_regex = re.compile("^(\+|-)\d{2}:\d{2}$")

    if offset_regex.match(offset) is None:
        raise SuspiciousOperation("Time offset is not valid")

    # Append time offsets
    start_date += " 00:00" + offset
    end_date += " 00:00" + offset

    company_departments = Department.objects.filter(
        active=True, company_id=logged.company_id)

    data = {'names': [], 'values': []}

    for dpmt in company_departments:
        time_total = TimeLog.objects.filter(
            task_id__projectDepartment_id__project_id=project,
            task_id__projectDepartment_id__department_id=dpmt,
            workDate__range=[start_date, end_date
                             ]).aggregate(Sum('duration'))["duration__sum"]
        if time_total is None:
            time_total = 0

        data['names'].append(dpmt.name)
        data['values'].append(time_total)

    return JsonResponse(data)
def delete_project_department(project_department, admin):
    """Deletes the relationship between a project and a department"""
    same_company_or_403(admin,project_department.department_id)
    same_company_or_403(admin,project_department.project_id)

    project_department.delete()
Exemplo n.º 23
0
def update_password(request, username):
    """
    url = employee/updatePassword/<username>

    parameters:
        password1: contraseña a establecer
        password2: repetición de la contraseña

    returns:
        {'success': true/false: 'errors': [...]}

    errors:
        'employeeCreation_formNotValid': si el formulario no es válido
        'employeeCreation_passwordsDontMatch' : si las contraseñas no coinciden

    template: ninguna (ajax)
    """

    # Check that the user is logged in and it's an administrator
    admin = get_admin_executive_or_403(request)
    employee = get_object_or_404(Employee,
                                 user__username=username,
                                 user__is_active=True)

    # Check that the admin has permission to view that employee
    same_company_or_403(admin, employee)

    if request.method == 'POST':
        # Process the form
        form = EmployeePasswordForm(request.POST)

        if form.is_valid():
            pass1 = form.cleaned_data["newpass1"]
            pass2 = form.cleaned_data["newpass2"]

            # Check password validation
            if not validate_pass(pass1):
                return JsonResponse({
                    'success': False,
                    'errors': ['newPasswordInvalid']
                })

            if pass1 != pass2:
                return JsonResponse({
                    'success':
                    False,
                    'errors': ['employeeCreation_passwordsDontMatch']
                })

            user = employee.user
            user.set_password(pass1)
            user.save()

            if form.cleaned_data["send_password_notification"]:
                notify_password_change(
                    user.email,
                    user.first_name,
                    newpass=pass1,
                    notifynewpass=form.cleaned_data["notify_new_pass"])

            return JsonResponse({'success': True, 'errors': []})
        else:
            # Invalid form
            return JsonResponse({
                'success': False,
                'errors': ['employeeCreation_formNotValid']
            })
    else:
        # Invalid HTTP operation
        raise SuspiciousOperation
Exemplo n.º 24
0
def edit(request, username):
    """
    url = employee/edit/<username>

    parameters/returns:
        form: formulario de edicion de datos de empleado

    errors:
        'employeeCreation_formNotValid': si el formulario no es válido

    template: employee_edit.html
    """

    # Check that the user is logged in and it's an administrator
    admin = get_admin_executive_or_403(request)
    employee = get_object_or_404(Employee, user__username=username)

    # Check that the admin has permission to view that employee
    same_company_or_403(admin, employee)

    if request.method == "GET":
        # Return a form filled with the employee's data
        form = EmployeeEditForm(
            initial={
                'first_name': employee.user.first_name,
                'last_name': employee.user.last_name,
                'email': employee.user.email,
                'identifier': employee.identifier,
                'phone': employee.phone,
                'price_per_hour': employee.price_per_hour
            })

        return render(
            request, 'employee/employee_edit.html', {
                'form': form,
                'picture': employee.picture,
                'username': username,
                'pass_form': EmployeePasswordForm(),
                'active': employee.user.is_active
            })

    elif request.method == "POST":
        # Process the received form

        form = EmployeeEditForm(request.POST, request.FILES)
        if form.is_valid():
            errors = []
            # Check that the price is OK
            if form.cleaned_data['price_per_hour'] <= 0:
                errors.append('employeeCreation_priceNotValid')

            # Check that the image is OK
            if not check_image(form, 'photo'):
                errors.append('employeeCreation_imageNotValid')

            # Check that the email is unique
            if not is_email_unique(
                    form.cleaned_data["email"]
            ) and employee.user.email != form.cleaned_data["email"]:
                errors.append('employeeCreation_emailNotUnique')

            if not errors:
                # Update employee data
                employee.identifier = form.cleaned_data["identifier"]
                employee.phone = form.cleaned_data["phone"]
                # New log if the salary has changed
                new_log = employee.price_per_hour != form.cleaned_data[
                    "price_per_hour"]

                employee.price_per_hour = form.cleaned_data["price_per_hour"]
                if form.cleaned_data["photo"]:
                    employee.picture = form.cleaned_data["photo"]

                # Update user data
                user = employee.user
                user.first_name = form.cleaned_data["first_name"]
                user.last_name = form.cleaned_data["last_name"]
                user.email = form.cleaned_data["email"]

                user.save()
                employee.save()

                # New log if the salary has changed
                if new_log:
                    EmployeeLog.objects.create(
                        employee_id=employee,
                        event="C",
                        price_per_hour=form.cleaned_data["price_per_hour"])
                return HttpResponseRedirect('/employee/view/' + username + '/')
            else:
                # There are errors
                return render(
                    request, 'employee/employee_edit.html', {
                        'form': form,
                        'errors': errors,
                        'picture': employee.picture,
                        'username': username,
                        'pass_form': EmployeePasswordForm(),
                        'active': employee.user.is_active
                    })

        else:
            # Form is not valid
            return render(
                request, 'employee/employee_edit.html', {
                    'form': form,
                    'picture': employee.picture,
                    'errors': ['employeeCreation_formNotValid'],
                    'username': username,
                    'pass_form': EmployeePasswordForm(),
                    'active': employee.user.is_active
                })
    else:
        raise PermissionDenied
Exemplo n.º 25
0
def ajax_productivity_per_task(request):
    """
    # Devuelve un objeto {'names': [dpto1, dpto2...], 'values': [tiempo1, tiempo2...]}

    # Parámetros obligatorios:
    # task_id - ID del task

    # Parámetros opcionales:
    # start_date - fecha en formato YYYY-MM-DD que indica el inicio de la medición. Por defecto, 30 días antes de la fecha actual.
    # end_date - fecha en formato YYYY-MM-DD que indica el final de la medición. Por defecto, fecha actual.
    # offset - desplazamiento (huso) horario en formato +/-HH:MM - Por defecto +00:00

    # Si se proporcionan pero no tienen el formato correcto se lanzará un error HTTP 400 Bad Request

    """
    # ------------------------- Cortesía de Agu ------------------------------

    actor=get_actor_or_403(request)

    if "task_id" not in request.GET:
        raise SuspiciousOperation

    task_id = request.GET["task_id"]
    task=get_object_or_404(Task, pk=task_id)
    actor=check_task(request,task,for_view=True)
    same_company_or_403(actor,task.actor_id)

    # Get and parse the dates and the offset
    start_date = request.GET.get("start_date", str(date.today() - timedelta(days=30)))
    end_date = request.GET.get("end_date", str(date.today()))
    date_regex = re.compile("^\d{4}-\d{2}-\d{2}$")

    if date_regex.match(start_date) is None or date_regex.match(end_date) is None:
        raise SuspiciousOperation("Start/end date are not valid")

    offset = request.GET.get("offset", "+00:00")
    offset_regex = re.compile("^(\+|-)\d{2}:\d{2}$")

    if offset_regex.match(offset) is None:
        raise SuspiciousOperation("Time offset is not valid")

    # Append time offsets
    start_date += " 00:00" + offset
    end_date += " 00:00" + offset

    # --------------------------------------------------------------------------
    dates = []
    str_dates = []

    d1 = datetime.strptime(start_date[0:19] + start_date[20:22], '%Y-%m-%d %H:%M%z')
    d2 = datetime.strptime(end_date[0:19] + end_date[20:22], '%Y-%m-%d %H:%M%z')
    delta = d2 - d1  # timedelta

    for i in range(delta.days + 1):
        str_dates.append((d1 + timedelta(days=i)).date().strftime("%Y-%m-%d"))
        dates.append(d1 + timedelta(days=i))

    data = {"days": str_dates, "production": [], "goal_evolution": []}
    index = 0
    # Save productivity for each  date
    # for each date, we will find the asociated timelog
    for log_date in dates:
        log = TimeLog.objects.filter(task_id=task_id, workDate__year=log_date.year, workDate__month=log_date.month,
                                     workDate__day=log_date.day).aggregate(
            total_duration=Sum(  F("duration")/60.0, output_field=FloatField()),
            total_produced_units=Sum(  F("produced_units"), output_field=FloatField()))
        if log is None:
            # Not work that day
            total_productivity = 0
            total_duration = 0
        else:
            total_produced_units = log["total_produced_units"]
            total_duration = log["total_duration"]
            if total_duration == 0 or total_duration is None:
                total_productivity = 0
            else:
                # If not produced but spent time, 0 productivity (you lazy guy...)
                if total_produced_units is None:
                    total_productivity = 0
                else:
                    total_productivity = total_produced_units/total_duration

        # Find the registry date of production goal evolution which is closest to the date
        expected_productivity = GoalEvolution.objects.filter(task_id_id=task_id,
                                                             registryDate__gte=log_date).first()

        # If we do not find the goal or if the date is after the last task update, it may be the current task goal
        if total_duration==0 or total_duration is None:
            expected_productivity=0
        else:
            if expected_productivity is None or task.registryDate <= log_date:
                expected_productivity = task.production_goal
            else:
                expected_productivity = expected_productivity.production_goal

        data["production"].append(default_round(total_productivity))
        data["goal_evolution"].append(default_round(expected_productivity))


    return JsonResponse(data)