예제 #1
0
    def download_xls(self, request, context=None, *args, **kwargs):
        from reports.utils import utc_to_local_time

        if context is None:
            context = request.session.get(self.get_session_key())

        if not context:
            messages.error(
                request, 'Данные отчета не найдены. Сначала выполните отчет')
            context = super(BaseReportView, self).get_context_data(**kwargs)
            context = self.get_default_context_data(**context)
            return self.render_to_response(context)

        dt = utcnow()
        if request.session.get('user'):
            user = User.objects.filter(
                is_active=True,
                wialon_username=self.request.session.get('user')).first()

            if user and user.timezone:
                dt = utc_to_local_time(dt, user.timezone)

        filename = 'report_%s.xls' % dt.strftime('%Y%m%d_%H%M%S')

        self.workbook = xlwt.Workbook()
        worksheet = self.workbook.add_sheet('Отчет')

        self.write_xls_data(worksheet, context)

        response = HttpResponse(content_type='application/vnd.ms-excel')
        response[
            'Content-Disposition'] = 'attachment; filename="%s"' % filename
        self.workbook.save(response)
        return response
예제 #2
0
    def render_faults(self, visit, index, unit_report_data):
        """Рассчитывает большие промежутки в сообщениях, ДУТ, завязанный на моточасы"""
        # первая геозона до включения массы
        if index == 0 and len(visit.odometers) < 3:
            return 'OK'

        faults = []
        # если топливо не меняется в течение геозоны, но при этом есть минимум 1км пробега:
        fuel_start, fuel_end = getattr(visit, 'start_fuel_level'), getattr(
            visit, 'end_fuel_level')
        odometer_start = getattr(visit, 'start_odometer')
        odometer_end = getattr(visit, 'end_odometer')

        if fuel_start is not None and fuel_end is not None and fuel_end - fuel_start == .0:
            if odometer_start is not None and odometer_end is not None \
                    and odometer_end - odometer_start > 1:
                faults.append([visit.dt_from, visit.dt_to, 'ДУТ'])

        prev_odometer = None
        for odometer in visit.odometers:
            if prev_odometer:
                if (odometer.dt - prev_odometer.dt
                    ).total_seconds() > self.module_fault_timeout:
                    # если период неисправности продолжается, то есть текущий диапазон тоже
                    # большой, как и предыдущий, тогда склеиваем 2 возможные неисправности
                    if faults and faults[-1][1] == prev_odometer.dt:
                        faults[-1][1] = odometer.dt
                    else:
                        faults.append(
                            [prev_odometer.dt, odometer.dt, 'бортовой блок'])

            prev_odometer = odometer

        for fault in faults:
            fault.append(
                format_timedelta((fault[1] - fault[0]).total_seconds()))
            fault[0] = date_format(
                utc_to_local_time(fault[0], self.user.timezone), 'H:i')
            fault[1] = date_format(
                utc_to_local_time(fault[1], self.user.timezone), 'H:i')

        if faults:
            return '\n'.join([
                '%s - %s (%s), %s' % (x[0], x[1], x[3], x[2]) for x in faults
            ])

        return 'OK'
def email_reports():
    print('Mailing monthly driving style total report...')
    reports = models.DrivingStyleTotalReportDelivery.objects.published()

    now = utcnow()

    for report in reports:
        print('Monthly VCHM Driving style total report %s' % report)

        for user in report.users.all():
            local_now = utc_to_local_time(now, user.timezone)

            if not user.email:
                print('Skipping user %s (no email)' % user)
                continue

            print('User %s' % user)
            sess_id = None

            try:
                # получаем отчеты через HTTP
                sess_id = get_wialon_session_key(user)
                # если время выполнения - 1е число месяца, то отчет сформируется за прошлый месяц,
                # иначе отчет подготовится за текущий месяц вчерашний день включительно.
                date_to = (local_now - datetime.timedelta(days=1)).date()
                date_from = date_to.replace(day=1)

                res = make_report(report,
                                  user,
                                  sess_id,
                                  date_from=date_from,
                                  date_to=date_to,
                                  attempts=0)

                filename = 'total_vchm_driving_report_%s.xls' % user.pk
                log = models.ReportEmailDeliveryLog(
                    user=user,
                    email=user.email,
                    report_type=EmailDeliveryReportTypeEnum.DRIVING_STYLE,
                    subject='Сводный отчет о качестве вождения (ВЧМ)',
                    body='Здравствуйте, %s. Отчет по вложении.' %
                    user.full_name)
                content = ContentFile(res.content)
                log.report.save(filename, content, save=False)
                log.save()
                log.send(reraise=True)

            except Exception as e:
                print('Error: %s' % e)
                send_trigger_email('Ошибка в работе системы рассылки отчетов',
                                   extra_data={
                                       'Exception': str(e),
                                       'Traceback': traceback.format_exc(),
                                       'report': report,
                                       'user': user
                                   })
            finally:
                if sess_id:
                    logout_session(user, sess_id)
예제 #4
0
    def post(self, request, **kwargs):
        units = []

        context = self.get_context_data(**kwargs)
        context.update({'now': utcnow(), 'units': units})

        units_els = request.data.xpath('/getMoving/unit')
        if not units_els:
            logout_session(request.user, self.sess_id)
            return error_response('Не указаны объекты типа unit',
                                  code='units_not_found')

        self.get_geozones_report_template_id()

        for unit_el in units_els:
            self.get_input_data(unit_el)
            self.unit_id = int(self.input_data.get('unit_id'))
            self.get_job()
            self.get_route()
            self.get_report_data()
            self.get_object_messages()

            self.ride_points = []
            unit_info = {
                'id':
                self.unit_id,
                'date_begin':
                utc_to_local_time(self.input_data['date_begin'],
                                  request.user.timezone),
                'date_end':
                utc_to_local_time(self.input_data['date_end'],
                                  request.user.timezone),
                'points':
                self.ride_points
            }

            self.prepare_geozones_visits()
            self.process_messages()
            self.report_post_processing(unit_info)
            self.update_job_points_cache()
            self.prepare_output_data()

            units.append(unit_info)

        logout_session(request.user, self.sess_id)
        return XMLResponse('ura/moving.xml', context)
예제 #5
0
    def get_last_data(self, unit_name):
        data = self.last_data.get(unit_name)
        if data and len(data) > 1:
            dt, place = data[0], data[2]

            if isinstance(dt, dict):
                dt = datetime.datetime.utcfromtimestamp(dt['v'])
                dt = utc_to_local_time(dt, self.user.timezone)
            else:
                dt = parse_wialon_report_datetime(dt)

            if isinstance(place, dict) and 't' in place:
                place = place['t']

            return dt, place

        return None, None
예제 #6
0
    def get(self, request, **kwargs):
        now = utc_to_local_time(utcnow(), request.user.timezone)
        local_dt_from = now.replace(hour=0, minute=0, second=0)
        local_dt_to = now.replace(hour=23, minute=59, second=59)
        user = User.objects.get(pk=1)
        sess_id = get_wialon_session_key(user)
        moving_service = self.service_class(
            user=user,
            local_dt_from=local_dt_from,
            local_dt_to=local_dt_to,
            sess_id=sess_id
        )
        moving_service.exec_report()
        moving_service.analyze()
        logout_session(user, sess_id)

        return success_response()
예제 #7
0
    def report_post_processing(self, unit_info):
        job_date_begin = utc_to_local_time(
            self.job.date_begin.replace(tzinfo=None),
            self.request.user.timezone)
        job_date_end = utc_to_local_time(
            self.job.date_end.replace(tzinfo=None), self.request.user.timezone)

        for point in self.ride_points:
            point['time_in'] = utc_to_local_time(
                datetime.datetime.utcfromtimestamp(point['time_in']),
                self.request.user.timezone)
            point['time_out'] = utc_to_local_time(
                datetime.datetime.utcfromtimestamp(point['time_out']),
                self.request.user.timezone)

            if point['time_in'] >= job_date_begin and point[
                    'time_out'] <= job_date_end:
                point['job_id'] = self.job.pk

        for row in self.report_data['unit_thefts']:
            volume = parse_float(row['c'][2])

            if volume > .0 and row['c'][1]:
                dt = utc_to_local_time(
                    parse_wialon_report_datetime(
                        row['c'][1]['t'] if isinstance(row['c'][1], dict
                                                       ) else row['c'][1]),
                    self.request.user.timezone)

                for point in self.ride_points:
                    if point['time_in'] <= dt <= point['time_out']:
                        point['params']['fuelDrain'] += volume
                        break

        for row in self.report_data['unit_fillings']:
            volume = parse_float(row['c'][1])

            if volume > .0:
                dt = utc_to_local_time(
                    parse_wialon_report_datetime(
                        row['c'][0]['t'] if isinstance(row['c'][0], dict
                                                       ) else row['c'][0]),
                    self.request.user.timezone)

                for point in self.ride_points:
                    if point['time_in'] <= dt <= point['time_out']:
                        point['params']['fuelRefill'] += volume
                        break

        # рассчитываем моточасы пропорционально интервалам
        for row in self.report_data['unit_engine_hours']:
            time_from = utc_to_local_time(
                parse_wialon_report_datetime(row['c'][0]['t'] if isinstance(
                    row['c'][0], dict) else row['c'][0]),
                self.request.user.timezone)

            time_until_value = row['c'][1]['t'] \
                if isinstance(row['c'][1], dict) else row['c'][1]

            if 'unknown' in time_until_value.lower():
                time_until = utc_to_local_time(self.input_data['date_end'],
                                               self.request.user.timezone)
            else:
                time_until = utc_to_local_time(
                    parse_wialon_report_datetime(time_until_value),
                    self.request.user.timezone)

            for point in self.ride_points:
                if point['time_in'] > time_until:
                    # дальнейшие строки точно не совпадут (виалон все сортирует по дате)
                    break

                # если интервал точки меньше даты начала моточасов, значит еще не дошли
                if point['time_out'] < time_from:
                    continue

                delta = min(time_until, point['time_out']) - max(
                    time_from, point['time_in'])
                # не пересекаются:
                if delta.total_seconds() <= 0:
                    continue

                point['params']['motoHours'] += delta.total_seconds()

        for row in self.report_data['unit_chronology']:
            row_data = row['c']
            if not isinstance(row_data[0], str):
                send_trigger_email(
                    'В хронологии первое поле отчета не строка!',
                    extra_data={
                        'POST': self.request.body,
                        'row_data': row_data,
                        'user': self.request.user
                    })
                continue

            time_from = utc_to_local_time(
                parse_wialon_report_datetime(row_data[1]['t'] if isinstance(
                    row_data[1], dict) else row_data[1]),
                self.request.user.timezone)

            time_until_value = row_data[2]['t'] \
                if isinstance(row_data[2], dict) else row_data[2]

            if 'unknown' in time_until_value.lower():
                time_until = self.input_data['date_end']
            else:
                time_until = utc_to_local_time(
                    parse_wialon_report_datetime(time_until_value),
                    self.request.user.timezone)

            for point in self.ride_points:
                if point['time_in'] > time_until:
                    # дальнейшие строки точно не совпадут (виалон все сортирует по дате)
                    break

                # если интервал точки меньше даты начала хронологии, значит еще не дошли
                if point['time_out'] < time_from:
                    continue

                delta = min(time_until, point['time_out']) - max(
                    time_from, point['time_in'])
                # не пересекаются:
                if delta.total_seconds() < 0:
                    continue

                if row_data[0].lower() in ('поездка', 'trip'):
                    point['params']['moveMinutes'] += delta.total_seconds()

        # рассчитываем время работы крановой установки пропорционально интервалам
        # (только лишь ради кэша, необходимости в расчетах нет)
        for row in self.report_data['unit_digital_sensors']:
            time_from = utc_to_local_time(
                parse_wialon_report_datetime(row['c'][0]['t'] if isinstance(
                    row['c'][0], dict) else row['c'][0]),
                self.request.user.timezone)

            time_until_value = row['c'][1]['t'] \
                if isinstance(row['c'][1], dict) else row['c'][1]

            if 'unknown' in time_until_value.lower():
                time_until = utc_to_local_time(self.input_data['date_end'],
                                               self.request.user.timezone)
            else:
                time_until = utc_to_local_time(
                    parse_wialon_report_datetime(time_until_value),
                    self.request.user.timezone)

            for point in self.ride_points:
                if point['time_in'] > time_until:
                    # дальнейшие строки точно не совпадут (виалон все сортирует по дате)
                    break

                # если интервал точки меньше даты начала моточасов, значит еще не дошли
                if point['time_out'] < time_from:
                    continue

                delta = min(time_until, point['time_out']) - max(
                    time_from, point['time_in'])
                # не пересекаются:
                if delta.total_seconds() <= 0:
                    continue

                point['params']['GPMTime'] += delta.total_seconds()

        for point in self.ride_points:
            point['params']['stopMinutes'] = (
                point['time_out'] - point['time_in']
            ).total_seconds() - point['params']['moveMinutes']
예제 #8
0
def utc_to_local(utc_dt, timezone):
    return utc_to_local_time(utc_dt, timezone)
예제 #9
0
    def report_post_processing(self, job_info):
        for race in job_info['races']:
            race['date_start'] = utc_to_local_time(
                datetime.datetime.utcfromtimestamp(race['date_start']),
                self.request.user.timezone
            )
            race['date_end'] = utc_to_local_time(
                datetime.datetime.utcfromtimestamp(race['date_end']),
                self.request.user.timezone
            )

            for point in race['points']:
                point['time_in'] = utc_to_local_time(
                    datetime.datetime.utcfromtimestamp(point['time_in']),
                    self.request.user.timezone
                )
                point['time_out'] = utc_to_local_time(
                    datetime.datetime.utcfromtimestamp(point['time_out']),
                    self.request.user.timezone
                )

        for row in self.report_data['unit_chronology']:
            row_data = row['c']

            try:
                if row_data[0].lower() not in ('поездка', 'trip'):
                    continue

            except AttributeError as e:
                send_trigger_email(
                    'Ошибка в работе интеграции Wialon', extra_data={
                        'Exception': str(e),
                        'Traceback': traceback.format_exc(),
                        'data': row_data,
                        'POST': self.request.body,
                        'user': self.request.user
                    }
                )

            time_from = utc_to_local_time(
                parse_wialon_report_datetime(
                    row_data[1]['t']
                    if isinstance(row_data[1], dict)
                    else row_data[1]
                ),
                self.request.user.timezone
            )

            time_until_value = row_data[2]['t'] \
                if isinstance(row_data[2], dict) else row_data[2]

            if 'unknown' in time_until_value.lower():
                time_until = self.input_data['date_end']
            else:
                time_until = utc_to_local_time(
                    parse_wialon_report_datetime(time_until_value),
                    self.request.user.timezone
                )

            for race in job_info['races']:
                for point in race['points']:
                    if point['time_in'] > time_until:
                        # дальнейшие строки точно не совпадут (виалон все сортирует по дате)
                        break

                    # если интервал точки меньше даты начала моточасов, значит еще не дошли
                    if point['time_out'] < time_from:
                        continue

                    delta = min(time_until, point['time_out']) - max(time_from, point['time_in'])
                    # не пересекаются:
                    if delta.total_seconds() < 0:
                        continue

                    point['params']['moveTime'] += delta.total_seconds()
예제 #10
0
    def get_context_data(self, **kwargs):
        kwargs = super(OverstatementsView, self).get_context_data(**kwargs)
        report_data = None
        form = kwargs['form']
        kwargs['today'] = datetime.date.today()
        stats = {
            'total': 0
        }

        if self.request.POST:
            report_data = []

            if form.is_valid():
                sess_id = self.request.session.get('sid')
                if not sess_id:
                    raise ReportException(WIALON_NOT_LOGINED)

                user = User.objects.filter(is_active=True) \
                    .filter(wialon_username=self.request.session.get('user')).first()
                if not user:
                    raise ReportException(WIALON_USER_NOT_FOUND)

                dt_from = local_to_utc_time(form.cleaned_data['dt_from'], user.timezone)
                dt_to = local_to_utc_time(
                    form.cleaned_data['dt_to'].replace(second=59), user.timezone
                )

                routes = {
                    x['id']: x for x in get_routes(sess_id, with_points=True)
                }
                units_dict = {x['id']: x for x in get_units(sess_id)}

                standard_job_templates = StandardJobTemplate.objects\
                    .filter(wialon_id__in=[str(x) for x in routes.keys()])\
                    .prefetch_related(
                        Prefetch(
                            'points',
                            StandardPoint.objects.filter(
                                Q(total_time_standard__isnull=False) |
                                Q(parking_time_standard__isnull=False)
                            ),
                            'points_cache'
                        )
                    )

                standards = {
                    int(x.wialon_id): {
                        'space_overstatements_standard': x.space_overstatements_standard
                        if x.space_overstatements_standard is not None else None,
                        'points': {
                            p.title: {
                                'total_time_standard': p.total_time_standard
                                if p.total_time_standard is not None else None,
                                'parking_time_standard': p.parking_time_standard
                                if p.parking_time_standard is not None else None
                            } for p in x.points_cache
                        }
                    } for x in standard_job_templates
                    if x.space_overstatements_standard is not None or x.points_cache
                }

                ura_user = user.ura_user if user.ura_user_id else user
                jobs = Job.objects \
                    .filter(
                        user=ura_user,
                        date_begin__gte=dt_from,
                        date_end__lte=dt_to,
                        route_id__in=list(routes.keys())
                    )\
                    .prefetch_related(
                        Prefetch(
                            'points', JobPoint.objects.order_by('id'), to_attr='cached_points'
                        )
                    )\
                    .order_by('date_begin', 'date_end')

                def get_car_number(unit_id, _units_dict):
                    return _units_dict.get(int(unit_id), {}).get('number', '')

                normal_ratio = 1 + (form.cleaned_data['overstatement_param'] / 100.0)

                for job in jobs:
                    spaces_total_time = .0
                    spaces = []
                    standard = standards.get(int(job.route_id))
                    if not standard:
                        print('No standards (job id=%s)' % job.pk)
                        continue

                    for point in job.cached_points:

                        if point.title.lower() == 'space':
                            spaces.append(point)
                            spaces_total_time += point.total_time

                        else:
                            point_standard = standard['points'].get(point.title)
                            if not point_standard:
                                continue

                            overstatement = .0
                            total_time = point.total_time / 60.0
                            if point_standard['total_time_standard'] is not None\
                                    and total_time / point_standard['total_time_standard'] \
                                    > normal_ratio:
                                overstatement += total_time - point_standard['total_time_standard']

                            parking_time = point.parking_time / 60.0
                            if point_standard['parking_time_standard'] is not None\
                                    and parking_time / point_standard['parking_time_standard'] \
                                    > normal_ratio:
                                overstatement += parking_time \
                                    - point_standard['parking_time_standard']

                            if overstatement > .0:
                                stats['total'] += overstatement / 60.0
                                row = self.get_new_grouping()
                                row['fact_period'] = '%s - %s' % (
                                    date(utc_to_local_time(
                                        point.enter_date_time.replace(tzinfo=None),
                                        user.timezone
                                    ), 'd.m.Y H:i:s'),
                                    date(utc_to_local_time(
                                        point.leave_date_time.replace(tzinfo=None),
                                        user.timezone
                                    ), 'd.m.Y H:i:s')
                                )

                                row['plan_period'] = '%s - %s' % (
                                    date(utc_to_local_time(
                                        job.date_begin.replace(tzinfo=None),
                                        user.timezone
                                    ), 'd.m.Y H:i:s'),
                                    date(utc_to_local_time(
                                        job.date_end.replace(tzinfo=None),
                                        user.timezone
                                    ), 'd.m.Y H:i:s')
                                )
                                row['route_id'] = job.route_id
                                row['point_name'] = point.title
                                row['point_type'] = get_point_type(point.title)
                                row['car_number'] = get_car_number(job.unit_id, units_dict)
                                row['driver_fio'] = job.driver_fio if job.driver_fio else ''
                                overstatement = round(overstatement / 60.0, 2) \
                                    if overstatement > 1.0 else round(overstatement / 60.0, 4)
                                row['overstatement'] = overstatement

                                report_data.append(row)

                    spaces_total_time /= 60.0
                    if spaces \
                            and standard['space_overstatements_standard'] is not None \
                            and spaces_total_time / standard['space_overstatements_standard'] \
                            > normal_ratio:
                        row = self.get_new_grouping()
                        # период пока не указываем, так как это по всему маршруту
                        row['fact_period'] = '%s - %s' % (
                            date(utc_to_local_time(
                                spaces[0].enter_date_time.replace(tzinfo=None),
                                user.timezone
                            ), 'd.m.Y H:i:s'),
                            date(utc_to_local_time(
                                spaces[-1].leave_date_time.replace(tzinfo=None),
                                user.timezone
                            ), 'd.m.Y H:i:s')
                        )
                        row['plan_period'] = '%s - %s' % (
                            date(utc_to_local_time(
                                job.date_begin.replace(tzinfo=None),
                                user.timezone
                            ), 'd.m.Y H:i:s'),
                            date(utc_to_local_time(
                                job.date_end.replace(tzinfo=None),
                                user.timezone
                            ), 'd.m.Y H:i:s')
                        )
                        row['route_id'] = job.route_id
                        row['point_name'] = 'SPACE'
                        row['point_type'] = '0'
                        row['car_number'] = get_car_number(job.unit_id, units_dict)
                        row['driver_fio'] = job.driver_fio if job.driver_fio else ''
                        row['overstatement'] = round((
                            spaces_total_time - standard['space_overstatements_standard']
                        ) / 60.0, 2)

                        report_data.append(row)
                        stats['total'] += row['overstatement']

            report_data = sorted(report_data, key=lambda k: k['fact_period'])

        if stats['total']:
            stats['total'] = round(stats['total'], 2)

        kwargs.update(
            stats=stats,
            report_data=report_data
        )

        return kwargs
예제 #11
0
    def get_context_data(self, **kwargs):
        kwargs = super(DischargeView, self).get_context_data(**kwargs)
        form = kwargs['form']
        report_data = None

        sess_id = self.request.session.get('sid')
        if not sess_id:
            raise ReportException(WIALON_NOT_LOGINED)

        try:
            units_list = get_units(sess_id, extra_fields=True)
        except WialonException as e:
            raise ReportException(str(e))

        kwargs['units'] = units_list

        if self.request.POST:
            if form.is_valid():
                report_data = OrderedDict()

                self.user = User.objects.filter(is_active=True)\
                    .filter(wialon_username=self.request.session.get('user')).first()

                if not self.user:
                    raise ReportException(WIALON_USER_NOT_FOUND)

                normal_ratio = 1 + (
                    form.cleaned_data['overspanding_percentage'] / 100)

                units_dict = OrderedDict((u['id'], u) for u in units_list)
                selected_unit = form.cleaned_data.get('unit')

                if selected_unit and selected_unit in units_dict:
                    units_dict = {selected_unit: units_dict[selected_unit]}

                jobs_count = len(units_dict)
                print('Всего ТС: %s' % jobs_count)

                dt_from_utc = local_to_utc_time(form.cleaned_data['dt_from'],
                                                self.user.timezone)
                dt_to_utc = local_to_utc_time(
                    form.cleaned_data['dt_to'].replace(hour=23,
                                                       minute=59,
                                                       second=59),
                    self.user.timezone)

                ura_user = self.user.ura_user if self.user.ura_user_id else self.user
                jobs = Job.objects.filter(user=ura_user,
                                          date_begin__lt=dt_to_utc,
                                          date_end__gt=dt_from_utc).order_by(
                                              'date_begin', 'date_end')

                jobs_cache = defaultdict(list)
                for job in jobs:
                    try:
                        jobs_cache[int(job.unit_id)].append(job)
                    except ValueError:
                        pass

                template_id = get_wialon_report_template_id(
                    'discharge_individual', self.user, sess_id)
                device_fields = defaultdict(lambda: {'extras': .0, 'idle': .0})

                i = 0
                for unit_id, unit in units_dict.items():
                    i += 1
                    unit_name = unit['name']
                    print('%s/%s) %s' % (i, jobs_count, unit_name))

                    # норматив потребления доп.оборудования, л / час
                    extras_values = [
                        x['v'] for x in unit.get('fields', [])
                        if x.get('n') == 'механизм'
                    ]
                    # норматив потребления на холостом ходу, л / час
                    idle_values = [
                        x['v'] for x in unit.get('fields', [])
                        if x.get('n') == 'хх'
                    ]

                    if extras_values:
                        try:
                            device_fields[unit_name]['extras'] = float(
                                extras_values[0])
                        except ValueError:
                            pass

                    if idle_values:
                        try:
                            device_fields[unit_name]['idle'] = float(
                                idle_values[0])
                        except ValueError:
                            pass

                    if unit_id not in report_data:
                        report_data[unit_id] = self.get_new_grouping()

                    report_row = report_data[unit_id]
                    report_row['unit_name'] = unit_name
                    report_row['unit_number'] = unit.get('number', '')
                    report_row['vehicle_type'] = unit.get('vehicle_type', '')

                    unit_jobs = jobs_cache.get(unit_id)
                    if not unit_jobs:
                        report_row['periods'].append(
                            self.get_new_period(dt_from_utc, dt_to_utc))
                    else:
                        if unit_jobs[0].date_begin > dt_from_utc:
                            # если начало периода не попадает на смену
                            report_row['periods'].append(
                                self.get_new_period(dt_from_utc,
                                                    unit_jobs[0].date_begin))

                        previous_job = None
                        for unit_job in unit_jobs:
                            # если между сменами есть перерыв, то тоже добавляем период
                            if previous_job and unit_job.date_begin > previous_job.date_end:
                                report_row['periods'].append(
                                    self.get_new_period(
                                        previous_job.date_end,
                                        unit_job.date_begin))

                            report_row['periods'].append(
                                self.get_new_period(unit_job.date_begin,
                                                    unit_job.date_end,
                                                    unit_job))

                            previous_job = unit_job

                        if unit_jobs[-1].date_end < dt_to_utc:
                            # если смена закончилась до конца периода
                            report_row['periods'].append(
                                self.get_new_period(unit_jobs[-1].date_end,
                                                    dt_to_utc))

                    # получим полный диапазон запроса
                    dt_from = int(
                        time.mktime(
                            report_row['periods'][0]['dt_from'].timetuple()))
                    dt_to = int(
                        time.mktime(
                            report_row['periods'][-1]['dt_to'].timetuple()))

                    cleanup_and_request_report(self.user, template_id, sess_id)

                    r = exec_report(self.user,
                                    template_id,
                                    sess_id,
                                    dt_from,
                                    dt_to,
                                    object_id=unit_id)

                    wialon_report_rows = {}
                    for table_index, table_info in enumerate(
                            r['reportResult']['tables']):
                        rows = get_report_rows(sess_id,
                                               table_index,
                                               table_info['rows'],
                                               level=2 if table_info['name']
                                               == 'unit_thefts' else 1)

                        wialon_report_rows[table_info['name']] = [
                            row['c'] for row in rows
                        ]

                    for period in report_row['periods']:
                        for row in wialon_report_rows.get('unit_trips', []):
                            row_dt_from, row_dt_to = self.parse_wialon_report_datetime(
                                row)
                            if row_dt_to is None:
                                row_dt_to = period['dt_to']

                            if period['dt_from'] < row_dt_from and period[
                                    'dt_to'] > row_dt_to:
                                delta = (min(row_dt_to, period['dt_to']) -
                                         max(row_dt_from, period['dt_from'])
                                         ).total_seconds()
                                if not delta:
                                    print('empty trip period')
                                    continue

                                trip_ratio = 1
                                total_delta = (row_dt_to -
                                               row_dt_from).total_seconds()
                                if total_delta > 0:
                                    trip_ratio = delta / total_delta
                                period['mileage'] += parse_float(
                                    row[3]) * trip_ratio
                                period['move_hours'] += parse_timedelta(row[4]).total_seconds()\
                                    * trip_ratio

                        for row in wialon_report_rows.get(
                                'unit_digital_sensors', []):
                            row_dt_from, row_dt_to = self.parse_wialon_report_datetime(
                                row)
                            if row_dt_to is None:
                                row_dt_to = period['dt_to']

                            if period['dt_from'] < row_dt_from and period[
                                    'dt_to'] > row_dt_to:
                                delta = min(row_dt_to, period['dt_to']) - \
                                        max(row_dt_from, period['dt_from'])
                                period[
                                    'extra_device_hours'] += delta.total_seconds(
                                    )

                        for row in wialon_report_rows.get('unit_thefts', []):
                            dt = parse_wialon_report_datetime(
                                row[1]['t'] if isinstance(row[1], dict
                                                          ) else row[1])
                            utc_dt = local_to_utc_time(dt, self.user.timezone)
                            if period['dt_from'] <= utc_dt <= period['dt_to']:

                                place = row[0]['t'] if isinstance(
                                    row[0], dict) else (row[0] or '')
                                if place and not period['discharge']['place']:
                                    period['discharge']['place'] = place

                                if not period['discharge']['dt']:
                                    period['discharge']['dt'] = dt

                                try:
                                    volume = float(
                                        row[2].split(' ')[0] if row[2] else .0)
                                except ValueError:
                                    volume = .0

                                period['discharge']['volume'] += volume
                                self.stats['discharge_total'] += volume
                                self.stats['overspanding_count'] += 1

                                period['details'].append({
                                    'place': place,
                                    'dt': dt,
                                    'volume': volume
                                })

                        extras_value = device_fields.get(unit_name,
                                                         {}).get('extras', .0)
                        idle_value = device_fields.get(unit_name,
                                                       {}).get('idle', .0)

                        for row in wialon_report_rows.get(
                                'unit_engine_hours', []):
                            row_dt_from, row_dt_to = self.parse_wialon_report_datetime(
                                row)
                            if row_dt_to is None:
                                row_dt_to = period['dt_to']

                            if period['dt_from'] < row_dt_from and period[
                                    'dt_to'] > row_dt_to:
                                delta = (min(row_dt_to, period['dt_to']) -
                                         max(row_dt_from, period['dt_from'])
                                         ).total_seconds()

                                if not delta:
                                    print('empty motohours period')
                                    continue

                                total_delta = (row_dt_to -
                                               row_dt_from).total_seconds()

                                period['moto_hours'] += delta
                                # доля моточасов в периоде и общих моточасов в строке
                                # данный множитель учтем в расчетах потребления
                                moto_ratio = 1
                                if total_delta > 0:
                                    moto_ratio = delta / total_delta

                                try:
                                    period['fact_dut'] += (float(
                                        parse_float(row[3])) if row[3] else
                                                           .0) * moto_ratio

                                except ValueError:
                                    pass

                                try:
                                    period['fact_mileage'] += (float(
                                        parse_float(row[4])) if row[4] else
                                                               .0) * moto_ratio

                                except ValueError:
                                    pass

                                try:
                                    period['idle_hours'] += (parse_timedelta(
                                        row[6]).total_seconds() if row[6] else
                                                             .0) * moto_ratio

                                except ValueError:
                                    pass

                        if idle_value:
                            period['fact_motohours'] = max(
                                period['moto_hours'] - period['move_hours'] -
                                period['extra_device_hours'],
                                .0) / 3600.0 * idle_value

                        if extras_value:
                            period['fact_extra_device'] = \
                                period['extra_device_hours'] / 3600.0 * extras_value

                        total_facts = period['fact_extra_device'] \
                            + period['fact_motohours'] \
                            + period['fact_mileage']

                        if total_facts:
                            ratio = period['fact_dut'] / total_facts
                            if ratio >= normal_ratio:
                                overspanding = period['fact_dut'] \
                                   - total_facts

                                period['overspanding'] = overspanding
                                self.stats[
                                    'overspanding_total'] += overspanding

                        period['dt_from'] = utc_to_local_time(
                            period['dt_from'], self.user.timezone)
                        period['dt_to'] = utc_to_local_time(
                            period['dt_to'], self.user.timezone)

                if report_data:
                    for k, v in report_data.items():
                        v['periods'] = [
                            p for p in v['periods']
                            if p['discharge']['volume'] > .0
                            or p['overspanding'] > 0
                        ]

                    report_data = OrderedDict(
                        (k, v) for k, v in report_data.items() if v['periods'])

        kwargs.update(enumerate=enumerate,
                      report_data=report_data,
                      today=datetime.date.today(),
                      stats=self.stats)

        return kwargs
def email_reports(period=None):
    print('Mailing %s driving style report...' % period)
    if not period or period not in ('daily', 'weekly', 'monthly'):
        print('Period not specified')
        return

    reports = models.DrivingStyleReportDelivery.objects.published()
    period_verbose = ''
    if period == 'daily':
        period_verbose = 'Ежедневный'
        reports = reports.filter(is_daily=True)
    elif period == 'weekly':
        period_verbose = 'Еженедельный'
        reports = reports.filter(is_weekly=True)
    elif period == 'monthly':
        period_verbose = 'Ежемесячный'
        reports = reports.filter(is_monthly=True)

    now = utcnow()

    for report in reports:
        print('%s VCHM Driving style report %s' % (period, report))

        for user in report.users.all():
            local_now = utc_to_local_time(now, user.timezone)

            if not user.email:
                print('Skipping user %s (no email)' % user)
                continue

            if local_now.hour != SEND_HOUR:
                print('Skipping user %s (%s != %s)' %
                      (user, local_now.hour, SEND_HOUR))
                continue

            print('User %s' % user)
            sess_id = None

            try:
                # получаем отчеты через HTTP
                sess_id = get_wialon_session_key(user)
                date_from = date_to = (local_now -
                                       datetime.timedelta(days=1)).date()

                if period == 'daily':
                    date_from = date_to
                elif period == 'weekly':
                    date_from = (local_now - datetime.timedelta(days=7)).date()
                elif period == 'monthly':
                    date_from = date_to.replace(day=1)

                res = make_report(report,
                                  user,
                                  sess_id,
                                  date_from=date_from,
                                  date_to=date_to,
                                  attempts=0)

                filename = '%s_vchm_driving_report_%s.xls' % (period, user.pk)
                log = models.ReportEmailDeliveryLog(
                    user=user,
                    email=user.email,
                    report_type=EmailDeliveryReportTypeEnum.DRIVING_STYLE,
                    subject='%s отчет о качестве вождения (ВЧМ)' %
                    period_verbose,
                    body='Здравствуйте, %s. Отчет по вложении.' %
                    user.full_name)
                content = ContentFile(res.content)
                log.report.save(filename, content)
                log.save()
                log.send(reraise=True)

            except Exception as e:
                print('Error: %s' % e)
                send_trigger_email('Ошибка в работе системы рассылки отчетов',
                                   extra_data={
                                       'Exception': str(e),
                                       'Traceback': traceback.format_exc(),
                                       'report': report,
                                       'user': user
                                   })
            finally:
                if sess_id:
                    logout_session(user, sess_id)
예제 #13
0
    def write_xls_data(self, worksheet, context):
        worksheet = super(VchmTaxiingView,
                          self).write_xls_data(worksheet, context)

        self.styles.update({
            'border_bold_left_style':
            xlwt.easyxf(
                'font: bold 1, height 200;'
                'borders: bottom thin, left thin, right thin, top thin;'
                'align: wrap on, vert centre, horiz left'),
            'border_bold_right_style':
            xlwt.easyxf(
                'font: bold 1, height 200;'
                'borders: bottom thin, left thin, right thin, top thin;'
                'align: wrap on, vert centre, horiz right')
        })

        worksheet.col(0).width = 3600
        worksheet.col(1).width = 5500
        worksheet.col(2).width = 5500
        worksheet.col(3).width = 5500
        worksheet.col(4).width = 3000
        worksheet.col(5).width = 3000
        worksheet.col(6).width = 3150
        worksheet.col(7).width = 3150
        worksheet.col(8).width = 3200
        worksheet.col(9).width = 3200
        worksheet.col(10).width = 3800
        worksheet.col(11).width = 3300
        worksheet.col(12).width = 4500
        worksheet.col(13).width = 3300
        worksheet.col(14).width = 3300
        worksheet.col(15).width = 3300
        worksheet.col(16).width = 3300
        worksheet.col(17).width = 3300
        worksheet.col(18).width = 3300
        worksheet.col(19).width = 7000

        # header
        x = 0
        if context.get('heading_data'):
            for key, value in context['heading_data'].items():
                x += 1
                worksheet.write(x,
                                0,
                                key + ' ',
                                style=self.styles['right_center_style'])
                worksheet.write_merge(x,
                                      x,
                                      1,
                                      self.xls_heading_merge - 1,
                                      value,
                                      style=self.styles['left_center_style'])
            x += 1
            worksheet.write_merge(x, x, 0, self.xls_heading_merge, '')

        headings = ('Гос № ТС', 'ФИО водителя', 'Наименование маршрута',
                    'Наименование\nгеозоны', 'Время\nвхода в\nгеозону',
                    'Время\nвыхода из\nгеозоны',
                    'Время\nнахождения\nв геозоне,\nчч:мм',
                    'Время\nв движении\nв рамках\nгеозоны,\nчч:мм',
                    'Время\nпростоя\nв рамках\nгеозоны,\nчч:мм',
                    'Время\nпростоя\nна холостом\nходу, чч:мм',
                    'Время\nпростоя\nс выключенным\nдвигателем,\nчч:мм',
                    'Время\nпростоя\nво время\nработы КМУ,\nчч:мм',
                    'Количество\nпростоев с\nдлительностью\n> 3 минут',
                    'Пробег\nв рамках\nгеозоны,\nкм',
                    'Расход\nтоплива\nв рамках\nгеозоны,\nл',
                    'Заправка\nв рамках\nгеозоны,\nл',
                    'Слив в\nрамках\nгеозоны,\nл',
                    'Перепробег\nпо маршрутам,\nкм',
                    'Перепростой\nпо маршрутам,\nч',
                    'Исправность оборудования')

        x += 1
        for y, heading in enumerate(headings):

            worksheet.write(x,
                            y,
                            heading,
                            style=self.styles['border_center_style'])

        worksheet.row(x).height = 1200

        stats = context.get('stats')
        if stats:
            x += 1
            worksheet.write_merge(x,
                                  x,
                                  0,
                                  3,
                                  'ИТОГО за смену',
                                  style=self.styles['border_bold_left_style'])
            worksheet.write(x,
                            4,
                            date_format(
                                utc_to_local_time(stats['dt_from_min'],
                                                  self.user.timezone), 'H:i'),
                            style=self.styles['border_bold_left_style'])
            worksheet.write(x,
                            5,
                            date_format(
                                utc_to_local_time(stats['dt_to_max'],
                                                  self.user.timezone), 'H:i'),
                            style=self.styles['border_bold_left_style'])
            worksheet.write(x,
                            6,
                            format_timedelta(stats['total_time']),
                            style=self.styles['border_bold_left_style'])
            worksheet.write(x,
                            7,
                            format_timedelta(stats['moving_time']),
                            style=self.styles['border_bold_left_style'])
            worksheet.write(x,
                            8,
                            format_timedelta(stats['parking_time']),
                            style=self.styles['border_bold_left_style'])
            worksheet.write(x,
                            9,
                            format_timedelta(stats['idle_time']),
                            style=self.styles['border_bold_left_style'])
            worksheet.write(x,
                            10,
                            format_timedelta(stats['idle_off_time']),
                            style=self.styles['border_bold_left_style'])
            worksheet.write(x,
                            11,
                            format_timedelta(stats['angle_sensor_time']),
                            style=self.styles['border_bold_left_style'])
            worksheet.write(x,
                            12,
                            stats['over_3min_parkings_count'],
                            style=self.styles['border_bold_right_style'])
            worksheet.write(x,
                            13,
                            floatcomma(stats['odometer'], -2),
                            style=self.styles['border_bold_right_style'])
            worksheet.write(x,
                            14,
                            floatcomma(stats['fuel_level_delta'], -2),
                            style=self.styles['border_bold_right_style'])
            worksheet.write(x,
                            15,
                            floatcomma(stats['refills_delta'], -2),
                            style=self.styles['border_bold_right_style'])
            worksheet.write(x,
                            16,
                            floatcomma(stats['discharge_delta'], -2),
                            style=self.styles['border_bold_right_style'])
            worksheet.write(x,
                            17,
                            floatcomma(stats['overstatement_mileage'], -2),
                            style=self.styles['border_bold_right_style'])
            worksheet.write(x,
                            18,
                            format_timedelta(stats['overstatement_time']),
                            style=self.styles['border_bold_left_style'])
            worksheet.write(x, 19, '', style=self.styles['border_left_style'])
            worksheet.row(x).height = 360

        for row in context['report_data']:
            x += 1
            worksheet.write(x,
                            0,
                            row['car_number'],
                            style=self.styles['border_left_style'])
            worksheet.write(x,
                            1,
                            row['driver_fio'],
                            style=self.styles['border_left_style'])
            worksheet.write(x,
                            2,
                            row['route_name'],
                            style=self.styles['border_left_style'])
            worksheet.write(x,
                            3,
                            row['point_name'],
                            style=self.styles['border_left_style'])
            worksheet.write(x,
                            4,
                            date_format(
                                utc_to_local_time(row['dt_from'],
                                                  self.user.timezone), 'H:i'),
                            style=self.styles['border_left_style'])
            worksheet.write(x,
                            5,
                            date_format(
                                utc_to_local_time(row['dt_to'],
                                                  self.user.timezone), 'H:i'),
                            style=self.styles['border_left_style'])
            worksheet.write(x,
                            6,
                            format_timedelta(row['total_time']),
                            style=self.styles['border_left_style'])
            worksheet.write(x,
                            7,
                            format_timedelta(row['moving_time']),
                            style=self.styles['border_left_style'])
            worksheet.write(x,
                            8,
                            format_timedelta(row['parking_time']),
                            style=self.styles['border_left_style'])
            worksheet.write(x,
                            9,
                            format_timedelta(row['idle_time']),
                            style=self.styles['border_left_style'])
            worksheet.write(x,
                            10,
                            format_timedelta(row['idle_off_time']),
                            style=self.styles['border_left_style'])
            worksheet.write(x,
                            11,
                            format_timedelta(row['angle_sensor_time']),
                            style=self.styles['border_left_style'])
            worksheet.write(x,
                            12,
                            row['over_3min_parkings_count'],
                            style=self.styles['border_right_style'])
            worksheet.write(x,
                            13,
                            floatcomma(row['odometer'], -2),
                            style=self.styles['border_right_style'])
            worksheet.write(x,
                            14,
                            floatcomma(row['fuel_level_delta'], -2),
                            style=self.styles['border_right_style'])
            worksheet.write(x,
                            15,
                            floatcomma(row['refills_delta'], -2),
                            style=self.styles['border_right_style'])
            worksheet.write(x,
                            16,
                            floatcomma(row['discharge_delta'], -2),
                            style=self.styles['border_right_style'])
            worksheet.write(x,
                            17,
                            floatcomma(row['overstatement_mileage'], -2),
                            style=self.styles['border_right_style'])
            worksheet.write(x,
                            18,
                            format_timedelta(row['overstatement_time']),
                            style=self.styles['border_left_style'])
            worksheet.write(x,
                            19,
                            row['faults'],
                            style=self.styles['border_left_style'])
            worksheet.row(x).height = 720

        return worksheet
예제 #14
0
    def update_last_sensor_data(self, report_row, attempt=0):

        date_slice_from = attempt * LAST_SIGNAL_STEP
        date_slice_to = (attempt + 1) * LAST_SIGNAL_STEP

        if date_slice_to > LAST_SIGNAL_UNTIL:
            return report_row

        now = local_date_to = utc_to_local_time(utcnow(), self.user.timezone)

        if date_slice_from:
            local_date_to = now - datetime.timedelta(days=date_slice_from)

        local_date_from = now - datetime.timedelta(days=date_slice_to)
        dt_from, dt_to = get_period(local_date_from, local_date_to,
                                    self.user.timezone)

        print('Пробуем период поиска последнего сигнала %s - %s (попытка %s)' %
              (local_date_from, local_date_to, attempt + 1))
        cleanup_and_request_report(self.user, self.sensors_template_id,
                                   self.sess_id)
        r = exec_report(self.user,
                        self.sensors_template_id,
                        self.sess_id,
                        dt_from,
                        dt_to,
                        object_id=report_row['unit_id'])

        for table_index, table_info in enumerate(r['reportResult']['tables']):
            label = table_info['label'].split('(')[0].strip()

            if table_info[
                    'name'] != 'unit_sensors_tracing' or label != report_row[
                        'sensor']:
                continue

            if table_info['rows'] == 0:
                return self.update_last_sensor_data(report_row,
                                                    attempt=attempt + 1)

            rows = get_report_rows(self.sess_id, table_index, rows=1, level=1)

            if not rows:
                return self.update_last_sensor_data(report_row,
                                                    attempt=attempt + 1)

            dt, place = rows[0]['c'][2], rows[0]['c'][4]

            if isinstance(dt, dict):
                dt = datetime.datetime.utcfromtimestamp(dt['v'])
                dt = utc_to_local_time(dt, self.user.timezone)
            else:
                dt = parse_wialon_report_datetime(dt)

            if isinstance(place, dict) and 't' in place:
                place = place['t']

            if place:
                report_row['place'] = place

            if dt:
                report_row['dt'] = dt
                report_row[
                    'sum_broken_work_time'] = self.get_sum_broken_work_time(
                        report_row['unit_id'],
                        local_to_utc_time(dt, self.user.timezone),
                        report_row['job_date_end'])
            return report_row

        return self.update_last_sensor_data(report_row, attempt=attempt + 1)
예제 #15
0
    def get_context_data(self, **kwargs):
        kwargs = super(DrivingStyleView, self).get_context_data(**kwargs)
        self.form = kwargs['form']
        kwargs['today'] = datetime.date.today()
        errors = []

        sess_id = self.request.session.get('sid')
        if not sess_id:
            raise ReportException(WIALON_NOT_LOGINED)

        try:
            units_list = get_units(sess_id, extra_fields=True)
        except WialonException as e:
            raise ReportException(str(e))

        kwargs['units'] = units_list

        if self.request.POST:
            report_data = None

            if self.form.is_valid():
                report_data = OrderedDict()

                self.user = User.objects.filter(is_active=True) \
                    .filter(wialon_username=self.request.session.get('user')).first()

                if not self.user:
                    raise ReportException(WIALON_USER_NOT_FOUND)

                units_dict = OrderedDict((u['id'], u) for u in units_list)
                selected_unit = self.form.cleaned_data.get('unit')

                if selected_unit and selected_unit in units_dict:
                    units_dict = {selected_unit: units_dict[selected_unit]}

                dt_from_utc = local_to_utc_time(
                    self.form.cleaned_data['dt_from'], self.user.timezone)
                dt_to_utc = local_to_utc_time(
                    self.form.cleaned_data['dt_to'].replace(second=59),
                    self.user.timezone)

                ura_user = self.user.ura_user if self.user.ura_user_id else self.user
                jobs = Job.objects.filter(user=ura_user,
                                          date_begin__lt=dt_to_utc,
                                          date_end__gt=dt_from_utc).order_by(
                                              'date_begin', 'date_end')

                jobs_cache = defaultdict(list)
                for job in jobs:
                    try:
                        jobs_cache[int(job.unit_id)].append(job)
                    except ValueError:
                        pass

                template_id = get_wialon_report_template_id(
                    'driving_style_individual', self.user, sess_id)

                jobs_count = len(units_dict)
                print('Всего ТС: %s' % jobs_count)

                mobile_vehicle_types = set()
                if self.user.wialon_mobile_vehicle_types:
                    mobile_vehicle_types = set(
                        x.strip()
                        for x in self.user.wialon_mobile_vehicle_types.lower(
                        ).split(','))

                i = 0
                for unit_id, unit in units_dict.items():
                    i += 1
                    unit_name = unit['name']
                    print('%s/%s) %s' % (i, jobs_count, unit_name))

                    vehicle_type = unit.get('vehicle_type', '').lower().strip()

                    if not vehicle_type or (mobile_vehicle_types
                                            and vehicle_type
                                            not in mobile_vehicle_types):
                        print('%s) Skip vehicle type "%s" of item %s' %
                              (i, vehicle_type, unit_name))
                        continue

                    if unit_id not in report_data:
                        report_data[unit_id] = self.get_new_grouping()

                    report_row = report_data[unit_id]
                    report_row['unit_name'] = unit_name
                    report_row['unit_number'] = unit.get('number', '')

                    unit_jobs = jobs_cache.get(unit_id)
                    if not unit_jobs:
                        report_row['periods'].append(
                            self.get_new_period(dt_from_utc, dt_to_utc))
                    else:
                        if unit_jobs[0].date_begin > dt_from_utc:
                            # если начало периода не попадает на смену
                            report_row['periods'].append(
                                self.get_new_period(dt_from_utc,
                                                    unit_jobs[0].date_begin))

                        previous_job = None
                        for unit_job in unit_jobs:
                            # если между сменами есть перерыв, то тоже добавляем период
                            if previous_job and unit_job.date_begin > previous_job.date_end:
                                report_row['periods'].append(
                                    self.get_new_period(
                                        previous_job.date_end,
                                        unit_job.date_begin))

                            report_row['periods'].append(
                                self.get_new_period(unit_job.date_begin,
                                                    unit_job.date_end,
                                                    unit_job))

                            previous_job = unit_job

                        if unit_jobs[-1].date_end < dt_to_utc:
                            # если смена закончилась до конца периода
                            report_row['periods'].append(
                                self.get_new_period(unit_jobs[-1].date_end,
                                                    dt_to_utc))

                    # получим полный диапазон запроса
                    dt_from = int(
                        time.mktime(
                            report_row['periods'][0]['dt_from'].timetuple()))
                    dt_to = int(
                        time.mktime(
                            report_row['periods'][-1]['dt_to'].timetuple()))

                    cleanup_and_request_report(self.user, template_id, sess_id)
                    r = exec_report(self.user,
                                    template_id,
                                    sess_id,
                                    dt_from,
                                    dt_to,
                                    object_id=unit_id)

                    try:
                        wialon_report_rows = {}
                        for table_index, table_info in enumerate(
                                r['reportResult']['tables']):
                            wialon_report_rows[
                                table_info['name']] = get_report_rows(
                                    sess_id,
                                    table_index,
                                    table_info['rows'],
                                    level=1)
                    except ReportException as e:
                        print('%s) Skip vehicle %s due to error' %
                              (i, unit_name))
                        errors.append(
                            ('%s %s' % (report_row['unit_name'],
                                        report_row['unit_number']), str(e)))
                        continue

                    for period in report_row['periods']:
                        for row in wialon_report_rows.get('unit_trips', []):
                            row_dt_from, row_dt_to = self.parse_wialon_report_datetime(
                                row['c'])
                            if row_dt_to is None:
                                row_dt_to = period['dt_to']

                            # дальнейшие строки точно не совпадут (виалон все сортирует по дате):
                            if row_dt_from > period['dt_to']:
                                break

                            # если конец поездки меньше даты начала периода, значит еще не дошли
                            if row_dt_to < period['dt_from']:
                                continue

                            delta = (min(row_dt_to, period['dt_to']) -
                                     max(row_dt_from,
                                         period['dt_from'])).total_seconds()

                            if delta <= 0:
                                print('empty trips period')
                                continue

                            period['total_time'] += delta

                        if period['total_time']:
                            for row in wialon_report_rows.get(
                                    'unit_ecodriving', []):
                                if period['t_from'] < row['t2'] and period[
                                        't_to'] > row['t1']:
                                    detail_data = {
                                        'speed': {
                                            'count': 0,
                                            'seconds': .0
                                        },
                                        'lights': {
                                            'count': 0,
                                            'seconds': .0
                                        },
                                        'belt': {
                                            'count': 0,
                                            'seconds': .0
                                        },
                                        'devices': {
                                            'count': 0,
                                            'seconds': .0
                                        },
                                        'dt_from': '',
                                        'dt_to': ''
                                    }
                                    violation = row['c'][1].lower(
                                    ) if row['c'][1] else ''
                                    if 'свет' in violation or 'фар' in violation:
                                        viol_key = 'lights'
                                    elif 'скорост' in violation or 'превышен' in violation:
                                        viol_key = 'speed'
                                    elif 'ремн' in violation or 'ремен' in violation:
                                        viol_key = 'belt'
                                    elif 'кму' in violation:
                                        viol_key = 'devices'
                                    else:
                                        viol_key = ''

                                    if viol_key:
                                        detail_data[
                                            'dt_from'] = parse_wialon_report_datetime(
                                                row['c'][2]['t'] if isinstance(
                                                    row['c'][2], dict
                                                ) else row['c'][2])
                                        detail_data[
                                            'dt_to'] = parse_wialon_report_datetime(
                                                row['c'][3]['t'] if isinstance(
                                                    row['c'][3], dict
                                                ) else row['c'][3])

                                        delta = min(row['t2'], period['t_to']) - \
                                            max(row['t1'], period['t_from'])
                                        detail_data[viol_key][
                                            'seconds'] = delta

                                        if self.form.cleaned_data[
                                                'include_details']:
                                            period['details'].append(
                                                detail_data)
                                        period['facts'][viol_key]['count'] += 1
                                        period['facts'][viol_key][
                                            'seconds'] += delta

                            for viol_key in ('speed', 'lights', 'belt',
                                             'devices'):
                                percentage = min(
                                    period['facts'][viol_key]['seconds'] /
                                    period['total_time'], 1.0) * 100
                                period['percentage'][viol_key] = percentage
                                period['rating'] -= percentage

                        period['dt_from'] = utc_to_local_time(
                            period['dt_from'], self.user.timezone)
                        period['dt_to'] = utc_to_local_time(
                            period['dt_to'], self.user.timezone)
                        period['rating'] = max(period['rating'], .0)

                for k, v in report_data.items():
                    v['periods'] = list(
                        filter(lambda p: p['total_time'], v.get('periods',
                                                                [])))

                report_data = OrderedDict(
                    (k, v) for k, v in report_data.items() if v['periods'])

            if errors:
                messages.error(self.request,
                               '<br>'.join(['%s: %s' % x for x in errors]))
                kwargs.update(messages=get_messages(self.request) or [])

            kwargs.update(report_data=report_data,
                          render_background=self.render_background,
                          enumerate=enumerate)

        return kwargs
예제 #16
0
    def get_context_data(self, **kwargs):
        kwargs = super(InvalidJobStartEndView, self).get_context_data(**kwargs)
        report_data = None
        form = kwargs['form']
        stats = {}
        kwargs['today'] = datetime.date.today()

        if self.request.POST:
            if form.is_valid():
                report_data = OrderedDict()
                sess_id = self.request.session.get('sid')
                if not sess_id:
                    raise ReportException(WIALON_NOT_LOGINED)

                user = User.objects.filter(is_active=True) \
                    .filter(wialon_username=self.request.session.get('user')).first()
                if not user:
                    raise ReportException(WIALON_USER_NOT_FOUND)

                dt_from = local_to_utc_time(form.cleaned_data['dt_from'],
                                            user.timezone)
                dt_to = local_to_utc_time(
                    form.cleaned_data['dt_to'].replace(second=59),
                    user.timezone)

                routes_dict = {
                    x['id']: x
                    for x in get_routes(sess_id, with_points=True)
                }
                units_dict = {x['id']: x for x in get_units(sess_id)}

                ura_user = user.ura_user if user.ura_user_id else user
                jobs = Job.objects.filter(
                    user=ura_user,
                    date_begin__gte=dt_from,
                    date_end__lte=dt_to,
                    route_id__in=list(
                        routes_dict.keys())).prefetch_related('points')

                if jobs:
                    report_data = dict(start=[], end=[])

                def get_car_number(unit_id, _units_dict):
                    return _units_dict.get(int(unit_id), {}).get('number', '')

                for job in jobs:
                    route = routes_dict.get(int(job.route_id))

                    if not route:
                        print('No route found! job_id=%s' % job.pk)
                        continue

                    elif not form.cleaned_data.get('include_fixed', False) \
                            and is_fixed_route(route['name']):
                        print('Fixed route was skipped. job_id=%s, route=%s' %
                              (job.pk, route['name']))
                        continue

                    route_points = route['points']
                    has_base = len(
                        list(
                            filter(lambda x: 'база' in x['name'].lower(),
                                   route_points))) > 0

                    points = list(job.points.order_by('id'))

                    if not points:
                        # кэша нет, пропускаем
                        print('No job moving cache! job_id=%s' % job.pk)
                        continue

                    start_point = points[0]
                    start_point_name = start_point.title.lower().strip()

                    if (has_base and 'база' not in start_point_name)\
                            or (
                                not has_base
                                and 'погрузк' not in start_point_name
                                and 'база' not in start_point_name
                            ):
                        row = self.get_new_start_grouping()

                        row['car_number'] = get_car_number(
                            job.unit_id, units_dict)
                        row['driver_fio'] = job.driver_fio.strip()
                        row['job_date_start'] = utc_to_local_time(
                            job.date_begin.replace(tzinfo=None), user.timezone)
                        row['point_type'] = get_point_type(start_point.title)
                        row['route_id'] = str(job.route_id)
                        row['route_title'] = routes_dict.get(
                            int(job.route_id), {}).get('name', '')
                        row['route_fact_start'] = start_point.title

                        if start_point_name == 'space':
                            row['fact_start'] = '%s, %s' % (start_point.lat,
                                                            start_point.lng)

                        report_data['start'].append(row)

                    if len(points) <= 1:
                        continue

                    end_point = points[-1]
                    if not end_point.enter_date_time:
                        continue

                    delta = (job.date_end -
                             end_point.enter_date_time).total_seconds() / 60.0
                    if delta < form.cleaned_data.get('job_end_timeout', 30.0):
                        continue

                    row = self.get_new_end_grouping()
                    row['car_number'] = get_car_number(job.unit_id, units_dict)
                    row['driver_fio'] = job.driver_fio.strip()
                    row['job_date_end'] = utc_to_local_time(
                        job.date_end.replace(tzinfo=None), user.timezone)
                    row['point_type'] = get_point_type(end_point.title)
                    row['point_title'] = end_point.title
                    row['route_id'] = str(job.route_id)
                    row['route_title'] = routes_dict.get(
                        int(job.route_id), {}).get('name', '')
                    row['fact_end'] = utc_to_local_time(
                        end_point.enter_date_time.replace(tzinfo=None),
                        user.timezone)
                    row['delta'] = round(delta / 60.0, 2)

                    report_data['end'].append(row)

        kwargs.update(stats=stats, report_data=report_data)

        return kwargs
예제 #17
0
    def get_context_data(self, **kwargs):
        kwargs = super(FaultsView, self).get_context_data(**kwargs)
        form = kwargs['form']
        kwargs['today'] = datetime.date.today()

        if self.request.POST:
            self.report_data = []

            if form.is_valid():
                self.sess_id = self.request.session.get('sid')
                if not self.sess_id:
                    raise ReportException(WIALON_NOT_LOGINED)

                self.user = User.objects.filter(is_active=True) \
                    .filter(wialon_username=self.request.session.get('user')).first()

                if not self.user:
                    raise ReportException(WIALON_USER_NOT_FOUND)

                report_date = form.cleaned_data['dt']
                job_extra_offset = datetime.timedelta(
                    seconds=form.cleaned_data['job_extra_offset'] * 60 * 60)

                units_list = get_units(self.sess_id)
                units_cache = {u['id']: u['name'] for u in units_list}

                dt_from_local = datetime.datetime.combine(
                    report_date, datetime.time(0, 0, 0))
                dt_to_local = datetime.datetime.combine(
                    report_date, datetime.time(23, 59, 59))
                dt_from_utc = local_to_utc_time(dt_from_local,
                                                self.user.timezone)
                dt_to_utc = local_to_utc_time(dt_to_local, self.user.timezone)

                self.sensors_template_id = get_wialon_report_template_id(
                    'sensors', self.user, self.sess_id)
                last_data_template_id = get_wialon_report_template_id(
                    'last_data', self.user, self.sess_id)

                ura_user = self.user.ura_user if self.user.ura_user_id else self.user
                jobs = Job.objects.filter(
                    user=ura_user,
                    date_begin__lt=dt_to_utc,
                    date_end__gt=dt_from_utc).order_by('unit_title')

                if jobs:
                    dt_from, dt_to = get_period(dt_from_local, dt_to_local,
                                                self.user.timezone)
                    cleanup_and_request_report(self.user,
                                               last_data_template_id,
                                               self.sess_id)
                    r = exec_report(self.user, last_data_template_id,
                                    self.sess_id, dt_from, dt_to)

                    for table_index, table_info in enumerate(
                            r['reportResult']['tables']):
                        rows = get_report_rows(self.sess_id,
                                               table_index,
                                               table_info['rows'],
                                               level=1)

                        if table_info['name'] == 'unit_group_location':
                            self.last_data = {
                                r['c'][0]: r['c'][1:]
                                for r in rows
                            }
                            break

                jobs_count = len(jobs)
                print('Всего ПЛ при анализе состояния оборудования: %s' %
                      jobs_count)
                for i, job in enumerate(jobs):
                    try:
                        unit_name = units_cache.get(int(job.unit_id))
                    except (ValueError, AttributeError, TypeError):
                        unit_name = ''

                    if not unit_name:
                        print('ТС не найден! %s' % job.unit_id)
                        continue

                    print('%s/%s) %s' % (i + 1, jobs_count, unit_name))
                    self.stats['total'].add(unit_name)

                    job_local_date_begin = utc_to_local_time(
                        job.date_begin - job_extra_offset, self.user.timezone)
                    job_local_date_to = utc_to_local_time(
                        job.date_end + job_extra_offset, self.user.timezone)
                    dt_from, dt_to = get_period(job_local_date_begin,
                                                job_local_date_to,
                                                self.user.timezone)
                    cleanup_and_request_report(self.user,
                                               self.sensors_template_id,
                                               self.sess_id)
                    r = exec_report(self.user,
                                    self.sensors_template_id,
                                    self.sess_id,
                                    dt_from,
                                    dt_to,
                                    object_id=int(job.unit_id))

                    report_tables = {}
                    for table_index, table_info in enumerate(
                            r['reportResult']['tables']):
                        label = table_info['label'].split('(')[0].strip()

                        if table_info['name'] != 'unit_sensors_tracing':
                            continue

                        report_tables[label] = {
                            'index': table_index,
                            'rows': table_info['rows']
                        }

                    if 'ВСЕ' not in report_tables:
                        self.add_report_row(job,
                                            unit_name,
                                            'Нет данных от блока мониторинга',
                                            sensor=None)
                        continue

                    has_movings = False
                    for field in self.known_sensors:
                        if field not in report_tables and field != 'ВСЕ':
                            # проверим, возможно датчик и не настроен
                            unit_sensors = self.get_unit_sensors(
                                int(job.unit_id))
                            if field.lower() in unit_sensors:
                                self.add_report_row(
                                    job,
                                    unit_name,
                                    'Нет данных по датчику "%s"' % field,
                                    sensor=field)
                            continue

                        # перебираем сначала более маленькие выборки, с целью ускорения работы
                        attempts = (10, 100,
                                    min(report_tables[field]['rows'], 10000))
                        for attempt, rows_limit in enumerate(attempts):

                            # таблицу ВСЕ придется взять целиком, для оценки движения
                            if field == 'ВСЕ' and attempt != len(attempts) - 1:
                                continue

                            rows = get_report_rows(
                                self.sess_id,
                                report_tables[field]['index'],
                                rows_limit,
                                level=1)

                            data = [r['c'] for r in rows]
                            if field == 'ВСЕ':
                                try:
                                    # есть ли какое-нибудь изменение скорости?
                                    has_movings = len(
                                        set(map(lambda x: x[4], data))) > 1
                                except IndexError:
                                    pass

                            analyze_result, values = self.analyze_sensor_data(
                                field, data)
                            if analyze_result in (None, True):
                                if analyze_result:
                                    # все отлично, данные меняются и они есть
                                    pass

                                elif analyze_result is None:
                                    # данных никаких нет!
                                    # Снова проверяем на наличие датчика в настройках
                                    # проверим, возможно датчик и не настроен
                                    unit_sensors = self.get_unit_sensors(
                                        int(job.unit_id))
                                    if field != 'ВСЕ' and field.lower(
                                    ) in unit_sensors:
                                        self.add_report_row(
                                            job,
                                            unit_name,
                                            'Нет данных по датчику "%s"' %
                                            field,
                                            sensor=field)

                                break

                            # если в последней попытке тоже не нашел различающиеся данные
                            elif not analyze_result and attempt == len(
                                    attempts) - 1:
                                try:
                                    value = float(list(values)[0])
                                except (ValueError, TypeError, IndexError,
                                        AttributeError):
                                    value = 0

                                if field in {
                                        'Ближний свет фар', 'Ремень',
                                        'Зажигание', 'GPS антенна'
                                }:
                                    if value == 1.0:
                                        value = 'Вкл'
                                    elif value == 0.0:
                                        value = 'Выкл'

                                # всегда включенный GPS - норма
                                if field == 'GPS антенна' and value == 'Вкл':
                                    continue

                                # всегда выключенное зажигание или свет фар
                                # для стоящей техники - норма
                                elif not has_movings and field in {
                                        'Зажигание', 'Ближний свет фар'
                                } and value == 'Выкл':
                                    continue

                                # всегда один и тот же уровень топлива для стоящей техники - норма
                                elif not has_movings and field == 'Датчик уровня топлива':
                                    continue

                                # всегда выключенный ремень для стоящей техники - норма
                                elif not has_movings and field == 'Ремень' and value == 'Выкл':
                                    continue

                                else:
                                    self.add_report_row(
                                        job,
                                        unit_name,
                                        'Датчик "%s" отправляет одинаковое '
                                        'значение (%s) в течение смены' %
                                        (field, value),
                                        sensor=field)

                total_post_processing = len(self.report_data)
                for i, report_row in enumerate(self.report_data):
                    if not report_row['sensor'] or not report_row['unit_id']:
                        continue

                    print('%s/%s) Последние данные сенсора "%s" %s' %
                          (i + 1, total_post_processing, report_row['sensor'],
                           report_row['unit_name']))
                    self.update_last_sensor_data(report_row)

        self.stats['total'] = len(self.stats['total'])
        self.stats['broken'] = len(self.stats['broken'])

        kwargs.update(stats=self.stats, report_data=self.report_data)

        return kwargs