def post(self, request, *args, **kwargs): doc = request.data.xpath('/unitsRequest') if len(doc) < 1: return error_response('Не найден объект unitsRequest', code='unitsRequest_not_found') doc = doc[0] doc_id = doc.get('idDoc', '') if not doc_id: return error_response('Не указан параметр idDoc', code='idDoc_not_found') try: org_id = int(doc.get('idOrg', '')) except ValueError: org_id = 0 sess_id = get_wialon_session_key(request.user) try: units = get_units(sess_id) except APIProcessError as e: return error_response(str(e), code=e.code) finally: logout_session(request.user, sess_id) context = self.get_context_data(**kwargs) context.update({ 'doc_id': doc_id, 'create_date': utcnow(), 'units': units, 'org_id': org_id }) return XMLResponse('ura/units.xml', context)
def forwards_func(apps, schema_editor): job_model = apps.get_model('ura', 'UraJob') user_model = apps.get_model('users', 'User') orgs = user_model.objects.filter(is_active=True, supervisor__isnull=False) units_cache = {} routes_cache = {} for org in orgs.iterator(): print('Organization %s' % org.username) sess_id = get_wialon_session_key(org) units = get_units(sess_id) units_cache[org.pk] = { u['id']: '%s (%s) [%s]' % (u['name'], u['number'], u['vin']) for u in units } routes = get_routes(sess_id) logout_session(org, sess_id) routes_cache[org.pk] = {r['id']: r['name'] for r in routes} sess_id = None print('Jobs count: %s' % job_model.objects.count()) i = 0 for job in job_model.objects.iterator(): i += 1 if not job.user_id: print('%s: Job %s missed due to lack of user' % (i, job.pk)) continue unit_title = route_title = None try: unit_id = int(job.unit_id) unit_title = units_cache.get(job.user_id, {}).get(unit_id) if unit_title: job.unit_title = unit_title except (ValueError, TypeError, AttributeError): pass try: route_id = int(job.route_id) route_title = routes_cache.get(job.user_id, {}).get(route_id) if route_title: job.route_title = route_title except (ValueError, TypeError, AttributeError): pass if unit_title or route_title: job.save() print('%s: unit=%s, route=%s (%s)' % (i, unit_title, route_title, job.created))
def get_context_data(self, **kwargs): 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)) self.default_unit = units_list[0]['id'] kwargs = super(VchmTaxiingView, self).get_context_data(**kwargs) form = kwargs['form'] kwargs['units'] = units_list if not self.request.POST: return kwargs if form.is_valid(): report_data = [] stats = { 'dt_from_min': None, 'dt_to_max': None, 'total_time': .0, 'moving_time': .0, 'parking_time': .0, 'idle_time': .0, 'idle_off_time': .0, 'angle_sensor_time': .0, 'over_3min_parkings_count': 0, 'odometer': .0, 'fuel_level_delta': .0, 'refills_delta': .0, 'discharge_delta': .0, 'overstatement_mileage': .0, 'overstatement_time': .0 } kwargs.update(report_data=report_data, stats=stats) 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) local_dt_from = datetime.datetime.combine(form.cleaned_data['dt'], datetime.time(0, 0, 0)) local_dt_to = datetime.datetime.combine(form.cleaned_data['dt'], datetime.time(23, 59, 59)) unit_id = form.cleaned_data.get('unit') self.units_dict = OrderedDict([(x['name'], x) for x in units_list if x['id'] == unit_id]) unit = list(self.units_dict.values())[0] routes = { x['id']: x for x in get_routes(sess_id, with_points=True) } 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 } service = MovingService(self.user, local_dt_from, local_dt_to, sess_id, object_id=unit_id, units_dict=self.units_dict, last_visit_allowance=60 * 10, devide_last_parking_by_motohours=True) service.exec_report() service.analyze() ura_user = self.user.ura_user if self.user.ura_user_id else self.user jobs = Job.objects.filter( user=ura_user, date_begin__gte=local_to_utc_time(local_dt_from, ura_user.timezone), date_end__lte=local_to_utc_time(local_dt_to, ura_user.timezone)) jobs_cache = {int(j.unit_id): j for j in jobs} normal_ratio = 1 + (form.cleaned_data.get( 'overstatement_param', DEFAULT_OVERSTATEMENT_NORMAL_PERCENTAGE) / 100.0) unit_report_data = service.report_data.get(unit['name']) if not unit_report_data: return kwargs kwargs.update(heading_data=OrderedDict(( ('Марка, модель', self.get_car_model(unit['name'])), ('Гос.номер', self.get_car_number(unit['name'])), ('VIN', self.get_car_vin(unit['name'])), ('Тип ТС', self.get_car_type(unit['name'])), ('Дата', date_format(form.cleaned_data['dt'], 'd.m.Y'))))) job = jobs_cache.get(unit['id']) standard = None if job: standard = standards.get(int(job.route_id)) for i, visit in enumerate(unit_report_data.geozones.target): try: next_visit = unit_report_data.geozones.target[i + 1] except IndexError: next_visit = None point_standard = None if standard: point_standard = standard.get('points', {}).get( visit.geozone_full, {}) if not point_standard: point_standard = standard.get('points', {}).get( visit.geozone, {}) total_standart = parking_standard = None if point_standard: total_standart = point_standard['total_time_standard'] * 60 parking_standard = point_standard[ 'parking_time_standard'] * 60 total_delta = (visit.dt_to - visit.dt_from).total_seconds() moving_delta = getattr(visit, 'trips_delta', .0) # переделал пока под расчет разницы между периодом нахождения и временем в # движении, так как в расчет стоянок иногда не принимаются концевые участки # длиной несколько минут # parking_delta = getattr(visit, 'parkings_delta', .0) parking_delta = max(.0, total_delta - moving_delta) motohours_delta = getattr(visit, 'motohours_delta', .0) idle_delta = getattr(visit, 'idle_delta', .0) off_delta = max(.0, total_delta - motohours_delta) angle_sensor_delta = getattr(visit, 'angle_sensor_delta', .0) refillings_volume = getattr(visit, 'refillings_volume', .0) discharges_volume = getattr(visit, 'discharges_volume', .0) overstatement_time = .0 if total_standart is not None \ and total_delta / total_standart > normal_ratio: overstatement_time += total_delta - total_standart elif parking_standard is not None \ and parking_delta / parking_standard > normal_ratio: overstatement_time += parking_delta - parking_standard point_name = self.get_point_name(visit.geozone) if not point_name: # если первая строка, и зона неизвестна, то это зона, где машина # находилась до включения блока (допущение, предложенное заказчиком) if i == 0 and next_visit: point_name = self.get_point_name(next_visit.geozone) else: point_name = 'Неизвестная' over_3min_parkings_count = 0 if point_name == 'Неизвестная': over_3min_parkings_count = len([ True for x in getattr(visit, 'parkings', []) if (x.dt_to - x.dt_from).total_seconds() > 3 * 60 ]) report_row = { 'car_number': self.get_car_number(unit['name']), 'driver_fio': job.driver_fio if job and job.driver_fio else 'Неизвестный', 'route_name': job.route_title if job and job.route_title else 'Неизвестный маршрут', 'point_name': point_name, 'dt_from': visit.dt_from, 'dt_to': visit.dt_to, 'total_time': total_delta, 'moving_time': moving_delta, 'parking_time': parking_delta, 'idle_time': idle_delta, 'idle_off_time': off_delta, 'angle_sensor_time': angle_sensor_delta, 'over_3min_parkings_count': over_3min_parkings_count, 'odometer': self.get_odometer(unit, visit), 'fuel_level_delta': self.get_fuel_delta(unit, visit), 'refills_delta': refillings_volume, 'discharge_delta': discharges_volume, 'overstatement_mileage': .0, 'overstatement_time': overstatement_time, 'faults': self.render_faults(visit, i, unit_report_data) } report_data.append(report_row) if stats['dt_from_min'] is None: stats['dt_from_min'] = visit.dt_from else: stats['dt_from_min'] = min(stats['dt_from_min'], visit.dt_from) if stats['dt_to_max'] is None: stats['dt_to_max'] = visit.dt_to else: stats['dt_to_max'] = max(stats['dt_to_max'], visit.dt_to) stats['total_time'] += total_delta stats['moving_time'] += moving_delta stats['parking_time'] += parking_delta stats['idle_time'] += idle_delta stats['idle_off_time'] += off_delta stats['angle_sensor_time'] += angle_sensor_delta stats['over_3min_parkings_count'] += over_3min_parkings_count stats['odometer'] += self.get_odometer(unit, visit) stats['fuel_level_delta'] += self.get_fuel_delta(unit, visit) stats['refills_delta'] += refillings_volume stats['discharge_delta'] += discharges_volume stats['overstatement_mileage'] += .0 stats['overstatement_time'] += overstatement_time return kwargs
def post(self, request, *args, **kwargs): jobs = [] jobs_els = request.data.xpath('/setJobs/job') sess_id = get_wialon_session_key(request.user) if jobs_els: for j in jobs_els: data = parse_xml_input_data(request, self.model_mapping, j) name = data.get('name') if not name: logout_session(request.user, sess_id) return error_response('Не указан параметр jobName', code='jobName_not_found') routes = get_routes(sess_id, with_points=True) units = get_units(sess_id) routes_ids = [x['id'] for x in routes] if data['route_id'] not in routes_ids: return error_response( 'Шаблон задания idRoute неверный или не принадлежит текущей организации', code='route_permission') units_cache = { u['id']: '%s (%s) [%s]' % (u['name'], u['number'], u['vin']) for u in units } try: data['unit_title'] = units_cache.get(int(data['unit_id'])) except (ValueError, TypeError, AttributeError): pass if not data['unit_title']: return error_response( 'Объект ID=%s не найден в текущем ресурсе организации' % data['unit_id'], code='unit_not_found_permission') routes_cache = {r['id']: r for r in routes} try: data['route_title'] = routes_cache.get( int(data['route_id']), {}).get('name') except (ValueError, TypeError, AttributeError): pass data['user'] = request.user self.job = self.model.objects.create(**data) register_job_notifications(self.job, sess_id, routes_cache=routes_cache) logout_session(request.user, sess_id) jobs.append(self.job) context = self.get_context_data(**kwargs) context.update({'now': utcnow(), 'acceptedJobs': jobs}) return XMLResponse('ura/ackjobs.xml', context)
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
def get_context_data(self, **kwargs): kwargs = super(VchmIdleTimesView, self).get_context_data(**kwargs) report_data = None form = kwargs['form'] 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 = [] 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) local_dt_from = datetime.datetime.combine( form.cleaned_data['dt_from'], datetime.time(0, 0, 0) ) local_dt_to = datetime.datetime.combine( form.cleaned_data['dt_to'], datetime.time(23, 59, 59) ) selected_unit = form.cleaned_data.get('unit') self.units_dict = OrderedDict( (x['name'], x) for x in units_list if not selected_unit or (selected_unit and x['id'] == selected_unit) ) routes = { x['id']: x for x in get_routes(sess_id, with_points=True) } 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=local_to_utc_time(local_dt_from, ura_user.timezone), date_end__lte=local_to_utc_time(local_dt_to, ura_user.timezone) ) jobs_cache = {int(j.unit_id): j for j in jobs} mobile_vehicle_types = set() vehtypes = user.wialon_mobile_vehicle_types or ura_user.wialon_mobile_vehicle_types if vehtypes: mobile_vehicle_types = set(x.strip() for x in vehtypes.lower().split(',')) service = MovingService( user, local_dt_from, local_dt_to, sess_id, object_id=selected_unit if selected_unit else None, units_dict=self.units_dict, calc_odometer=False ) service.exec_report() service.analyze() normal_ratio = 1 + ( form.cleaned_data.get( 'overstatement_param', DEFAULT_OVERSTATEMENT_NORMAL_PERCENTAGE ) / 100.0 ) total_count = len(self.units_dict) i = 0 for unit in self.units_dict.values(): i += 1 print('%s/%s: %s' % (i, total_count, unit['name'])) vehicle_type = unit['vehicle_type'].lower() if mobile_vehicle_types and vehicle_type \ and vehicle_type not in mobile_vehicle_types: print('%s) Skip vehicle type "%s" of item %s' % ( i, vehicle_type, unit['name'] )) continue job = jobs_cache.get(unit['id']) standard = None fixed_route = False if job: fixed_route = is_fixed_route(job.route_title) standard = standards.get(int(job.route_id)) unit_report_data = service.report_data.get(unit['name'], {}) for visit in unit_report_data.geozones.target: total_standart = parking_standard = space_standard = None point_standard = {} if standard: point_standard = standard.get('points', {}).get(visit.geozone_full, {}) if not point_standard: point_standard = standard.get('points', {}).get(visit.geozone, {}) if fixed_route: point_standard['space_time_standard'] = form.cleaned_data.get( 'default_space_time_standard', DEFAULT_SPACE_TOTAL_TIME_STANDARD_MINUTES ) if point_standard.get('total_time_standard'): total_standart = point_standard['total_time_standard'] * 60 if point_standard.get('parking_time_standard'): parking_standard = point_standard['parking_time_standard'] * 60 if point_standard.get('space_time_standard'): space_standard = point_standard['space_time_standard'] * 60 overstatement = .0 total_time = (visit.dt_to - visit.dt_from).total_seconds() parking_time = getattr(visit, 'parkings_delta', .0) if visit.geozone.lower() == 'space': if space_standard is not None \ and total_time / space_standard > normal_ratio: overstatement += total_time - space_standard elif total_standart is not None \ and total_time / total_standart > normal_ratio: overstatement += total_time - total_standart if parking_standard is not None \ and parking_time / parking_standard > normal_ratio: overstatement += parking_time - parking_standard if overstatement > .0 and total_time < 86395: # суточный простой исключаем row = dict() row['driver_fio'] = job.driver_fio \ if job and job.driver_fio else 'Неизвестный' row['car_number'] = self.get_car_number(unit['name']) row['car_vin'] = self.get_car_vin(unit['name']) row['car_type'] = self.get_car_type(unit['name']) row['route_name'] = job.route_title \ if job and job.route_title else 'Неизвестный маршрут' row['point_name'] = self.get_point_name(visit.geozone) row['total_time'] = total_time row['parking_time'] = parking_time row['off_motor_time'] = max(total_time - getattr( visit, 'motohours_delta', .0 ), .0) row['idle_time'] = sum([ (x.dt_to - x.dt_from).total_seconds() for x in getattr(visit, 'idle_times', []) ]) row['gpm_time'] = getattr( visit, 'angle_sensor_delta', .0 ) row['parkings'] = getattr(visit, 'parkings', []) row['overstatement'] = round(overstatement) report_data.append(row) kwargs.update( report_data=report_data ) return kwargs
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
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 get_context_data(self, **kwargs): kwargs = super(VchmDrivingStyleView, self).get_context_data(**kwargs) self.is_total = bool(self.request.POST.get('total_report')) total_report_data = [] form = kwargs['form'] 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 not self.request.POST: return kwargs units_dict = {u['name']: u for u in units_list} if form.is_valid(): if self.is_total: user = User.objects.filter( is_active=True, username=self.request.session.get('user') ).first() if not user: raise ReportException(WIALON_USER_NOT_FOUND) report_users = UserTotalReportUser.objects\ .published()\ .order_by('ordering')\ .filter(executor_user=user)\ .select_related('report_user', 'report_user__ura_user') users = set(user.report_user for user in report_users) else: user = User.objects.filter( is_active=True, wialon_username=self.request.session.get('user') ).first() if not user: raise ReportException(WIALON_USER_NOT_FOUND) users = {user} dt_from = local_to_utc_time(datetime.datetime.combine( form.cleaned_data['dt_from'], datetime.time(0, 0, 0) ), user.timezone) dt_to = local_to_utc_time(datetime.datetime.combine( form.cleaned_data['dt_to'], datetime.time(23, 59, 59) ), user.timezone) for user in users: report_data = [] print('Evaluating report for user %s' % user) ura_user = user.ura_user if user.ura_user_id else user print('URA user is %s' % ura_user) if self.request.POST.get('total_report'): sess_id = get_wialon_session_key(user) 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 units_dict = {u['name']: u for u in units_list} jobs = Job.objects\ .filter(user=ura_user, date_begin__lt=dt_to, date_end__gt=dt_from) if form.cleaned_data.get('unit'): jobs = jobs.filter(unit_id=str(form.cleaned_data['unit'])) self.driver_cache = { j.driver_id: j.driver_fio for j in jobs if j.driver_fio.lower() != 'нет в.а.' } self.driver_id_cache = { int(j.unit_id): j.driver_id for j in jobs if j.driver_fio.lower() != 'нет в.а.' } template_id = get_wialon_report_template_id('driving_style', user, sess_id) mobile_vehicle_types = set() if user.wialon_mobile_vehicle_types: mobile_vehicle_types = set( x.strip() for x in user.wialon_mobile_vehicle_types.lower().split(',') ) cleanup_and_request_report(user, template_id, sess_id) report_kwargs = {} if form.cleaned_data.get('unit'): report_kwargs['object_id'] = form.cleaned_data['unit'] print('Executing report...') r = exec_report( user, template_id, sess_id, int(time.mktime(dt_from.timetuple())), int(time.mktime(dt_to.timetuple())), **report_kwargs ) 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=2 if table_info['name'] == 'unit_group_ecodriving' else 1 ) self.mileage_cache = { row['c'][0]: parse_float(row['c'][1], default=.0) for row in wialon_report_rows.get('unit_group_trips', []) } self.duration_cache = { row['c'][0]: parse_timedelta(row['c'][2]).total_seconds() for row in wialon_report_rows.get('unit_group_trips', []) } i = 0 for row in wialon_report_rows.get('unit_group_ecodriving', []): i += 1 violations = [ self.parse_report_row(x['c'], user, total=False) for x in row['r'] ] row = self.parse_report_row(row['c'], user, total=True) unit = units_dict.get(row.unit_name) if not unit: print('%s) Unit not found: %s' % (i, row.unit_name)) continue vehicle_type = unit['vehicle_type'].lower() if mobile_vehicle_types and vehicle_type \ and vehicle_type not in mobile_vehicle_types: print('%s) Skip vehicle type "%s" of item %s' % ( i, vehicle_type, row.unit_name )) continue print('%s) Processing %s' % (i, row.unit_name)) ecodriving = get_drive_rank_settings(unit['id'], sess_id) ecodriving = {k.lower(): v for k, v in ecodriving.items()} report_row = self.new_grouping(row, unit) # собственно расчеты метрик for violation in violations: verbose = violation.violation_name violation_name = '' violation_scope = 'violations_measures' if 'превышение скорости' in verbose: if 'cреднее' in verbose or 'среднее' in verbose: violation_name = 'avg_overspeed' elif 'опасное' in verbose: violation_name = 'critical_overspeed' elif 'ремень' in verbose or 'ремня' in verbose: violation_name = 'belt' elif 'фар' in verbose: violation_name = 'lights' elif 'кму' in verbose or 'стрел' in verbose: violation_name = 'jib' elif 'разгон' in verbose or 'ускорение' in verbose: violation_scope = 'per_100km_count' violation_name = 'accelerations' elif 'торможение' in verbose: violation_scope = 'per_100km_count' violation_name = 'brakings' elif 'поворот' in verbose: violation_scope = 'per_100km_count' violation_name = 'turns' if not violation_name: print( '%s) %s: unknown violaton name %s' % ( i, row.unit_name, verbose ) ) continue scope = report_row[violation_scope][violation_name] if violation_scope == 'per_100km_count': scope['count'] += violation.violation_count / row.mileage * 100 scope['total_count'] += violation.violation_count else: scope['count'] += violation.violation_count scope['total_time_percentage'] += ( violation.duration / row.duration * 100 ) scope['time_sec'] += violation.duration # суммируем штрафы rating_violation_name = violation_name if 'overspeed' in violation_name: rating_violation_name = 'overspeed' # извлечем настройки объектов и узнаем, нужно ли рассчитывать # относительно пробега settings = ecodriving.get(verbose) devider = 1 if settings and settings.get('flags', 0) in (2, 3, 7, 10): devider = max(1.0, row.mileage) fine = violation.fine / devider report_row['rating'][rating_violation_name]['fine'] += fine report_row['rating_total']['avg']['fine'] += fine if rating_violation_name in ('belt', 'lights', 'jib', 'brakings'): report_row['rating_total']['critical_avg']['fine'] += fine # расчет статистики (рейтинга) for key in report_row['rating']: scope = report_row['rating'][key] scope['rating'] = self.calculate_rating(scope['fine']) report_row['rating_total']['avg']['rating'] = self.calculate_rating( report_row['rating_total']['avg']['fine'] ) report_row['rating_total']['critical_avg']['rating'] = self.calculate_rating( report_row['rating_total']['critical_avg']['fine'] ) report_data.append(report_row) # группируем строки по нарушителям и сортируем, самых нарушающих наверх groups = defaultdict(lambda: { 'rows': [], 'driver_id': '', 'driver_fio': '', 'company_name': '' }) # сначала отсортируем без группировки, # чтобы внутри групп была правильная сортировка report_data = sorted( report_data, key=lambda x: x['rating_total']['critical_avg']['rating'] ) for row in report_data: group = groups[row['driver_id']] group['driver_id'] = row['driver_id'] group['driver_fio'] = self.driver_cache.get(row['driver_id'], DRIVER_NO_NAME) group['company_name'] = user.company_name or user.wialon_resource_name or '' group['stats'] = self.new_grouping() group['rows'].append(row) # собираем суммарную статистику по каждому водителю report_data = list(groups.values()) for group in report_data: for row in group['rows']: group['stats']['total_mileage'] += row['total_mileage'] group['stats']['total_duration'] += row['total_duration'] for field in ('avg_overspeed', 'critical_overspeed', 'belt', 'lights', 'jib'): for row in group['rows']: group['stats']['violations_measures'][field]['count'] += \ row['violations_measures'][field]['count'] group['stats']['violations_measures'][field]['time_sec'] += \ row['violations_measures'][field]['time_sec'] group['stats']['violations_measures'][field]['total_time_percentage'] = \ group['stats']['violations_measures'][field]['time_sec'] \ / group['stats']['total_duration'] * 100.0 for field in ('brakings', 'accelerations', 'turns'): for row in group['rows']: group['stats']['per_100km_count'][field]['total_count'] += \ row['per_100km_count'][field]['total_count'] group['stats']['per_100km_count'][field]['count'] = \ group['stats']['per_100km_count'][field]['total_count'] \ / group['stats']['total_mileage'] * 100 for field in ( 'overspeed', 'belt', 'lights', 'brakings', 'accelerations', 'turns', 'jib' ): for row in group['rows']: fine = row['rating'][field]['fine'] group['stats']['rating'][field]['fine'] += fine group['stats']['rating_total']['avg']['fine'] += fine if field in ('belt', 'lights', 'jib', 'brakings'): group['stats']['rating_total']['critical_avg']['fine'] += fine # расчет статистики (рейтинга) for key in group['stats']['rating']: scope = group['stats']['rating'][key] scope['rating'] = self.calculate_rating(scope['fine']) group['stats']['rating_total']['avg']['rating'] = self.calculate_rating( group['stats']['rating_total']['avg']['fine'] ) group['stats']['rating_total']['critical_avg']['rating'] = \ self.calculate_rating( group['stats']['rating_total']['critical_avg']['fine'] ) total_report_data.extend(report_data) # финально отсортируем всю группу total_report_data = sorted( total_report_data, key=lambda x: x['stats']['rating_total']['critical_avg']['rating'] ) kwargs['report_data'] = total_report_data return kwargs
def __init__(self, user, local_dt_from, local_dt_to, sess_id, object_id=None, units_dict=None, tables=None, calc_odometer=True, calc_idle=True, first_visit_allowance=60 * 3, last_visit_allowance=60 * 3, devide_last_parking_by_motohours=False): self.user = user self.local_dt_from = local_dt_from self.local_dt_to = local_dt_to self.utc_dt_from = local_to_utc_time(local_dt_from, self.user.timezone) self.utc_dt_to = local_to_utc_time(local_dt_to, self.user.timezone) self.utc_timestamp_from = int(time.mktime( self.utc_dt_from.timetuple())) self.utc_timestamp_to = int(time.mktime(self.utc_dt_to.timetuple())) self.object_id = object_id self.sess_id = sess_id self.mobile_vehicle_types = set() self.calc_odometer = calc_odometer self.calc_idle = calc_idle # допущения в секундах для первого и последнего визита, если больше, то вставляется space self.first_visit_allowance = first_visit_allowance self.last_visit_allowance = last_visit_allowance self.devide_last_parking_by_motohours = devide_last_parking_by_motohours self.tables = [] all_tables = [ x['name'] for x in MOVING_SERVICE_MAPPING.values() if x['level'] > 0 ] if tables is not None: assert isinstance(tables, (list, dict, set)) self.tables = list(filter(lambda x: x in all_tables, tables)) else: self.tables = all_tables if units_dict is None: units_list = get_units(sess_id) self.units_dict = {u['name']: u for u in units_list} else: self.units_dict = units_dict self.report_data = OrderedDict() for unit in self.units_dict.values(): self.report_data[unit['name']] = ReportUnit(unit) if self.user.wialon_mobile_vehicle_types: self.mobile_vehicle_types = set( x.strip() for x in self.user.wialon_mobile_vehicle_types.lower().split( ',')) self.script_time_from = datetime.datetime.now() self.jobs_cache = {} self.routes_cache = {} self.init_caches()
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
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