def post(self, request, *args, **kwargs): doc = request.data.xpath('/routesRequest') if len(doc) < 1: return error_response('Не найден объект routesRequest', code='routesRequest_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: routes = get_routes(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(), 'routes': routes, 'org_id': org_id }) return XMLResponse('ura/routes.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_route(self): routes_list = get_routes(self.sess_id, with_points=True) routes_dict = {x['id']: x for x in routes_list} try: self.route = routes_dict[int(self.job.route_id)] except (ValueError, KeyError): raise APIProcessError( 'Маршрут id=%s не найден среди известных маршрутов' % self.job.route_id) self.route_point_names = [x['name'] for x in self.route['points'] ] if self.route else [] return self.route
def init_caches(self): 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=self.utc_dt_from, date_end__lte=self.utc_dt_to) if len(self.units_dict) < 10: jobs = jobs.filter( unit_id__in=[str(x['id']) for x in self.units_dict.values()]) self.jobs_cache = {int(j.unit_id): j for j in jobs} self.routes_cache = { x['id']: x for x in get_routes(self.sess_id, with_points=True) } self.print_time_needed('Init caches')
def route_overstatement_notification(job, sess_id, routes_cache=None, job_template=None): """7. Превышение времени нахождения на маршруте""" if not job_template: raise NotificationError( 'Не найден норматив шаблона задания. ID ПЛ=%s' % job.pk) if not routes_cache: routes = get_routes(sess_id, with_points=True) routes_cache = {r['id']: r for r in routes} dt_from = int(time.mktime(job.date_begin.timetuple())) dt_to = int(time.mktime(job.date_end.timetuple())) route = routes_cache.get(int(job.route_id), {}) # ищем только маршруты geozones = list( filter(lambda x: 'маршрут' in x['name'].lower(), route.get('points', []))) if not route or not geozones: raise NotificationError( 'Маршрут неизвестен или отсутствуют геозоны. ID ПЛ=%s' % job.pk) for geozone in geozones: standard = StandardPoint.objects.filter( wialon_id=geozone['id'].strip()).first() if not standard or not standard.total_time_standard: print( 'Не найдена геозона или отсутствует норматив нахождения для нее. ' 'ID ПЛ=%s, ID геозоны=%s' % (job.pk, geozone['id'])) continue data = { 'itemId': get_wialon_report_resource_id(job.user, sess_id), 'id': 0, 'callMode': 'create', 'n': 'Превысил время нахождения на маршруте %s' % geozone['name'], 'txt': '%UNIT% %CURR_TIME% превысил время нахождения (' + str(standard.total_time_standard) + ' мин.) на маршруте ' + geozone['name'], # время активации (UNIX формат) 'ta': dt_from, # время деактивации (UNIX формат) 'td': dt_to, # максимальное количество срабатываний (0 - не ограничено) 'ma': 0, # таймаут срабатывания(секунд) 'cdt': 10, # максимальный временной интервал между сообщениями (секунд) 'mmtd': 60 * 60, # минимальная продолжительность тревожного состояния (секунд) 'mast': int(standard.total_time_standard * 60), # минимальная продолжительность предыдущего состояния (секунд) 'mpst': 10, # период контроля относительно текущего времени (секунд) 'cp': 24 * 60 * 60, # флаги 'fl': 0, # часовой пояс 'tz': get_wialon_timezone_integer(job.user.timezone or get_current_timezone()), # язык пользователя (двухбуквенный код) 'la': 'ru', # массив ID объектов/групп объектов 'un': [int(job.unit_id)], 'sch': { 'f1': 0, 'f2': 0, 't1': 0, 't2': 0, 'm': 0, 'y': 0, 'w': 0 }, 'ctrl_sch': { 'f1': 0, 'f2': 0, 't1': 0, 't2': 0, 'm': 0, 'y': 0, 'w': 0 }, 'trg': { 't': 'geozone', 'p': { 'sensor_type': '', 'sensor_name_mask': '', 'lower_bound': 0, 'upper_bound': 0, 'merge': 0, 'geozone_ids': str(geozone['id'].split('-')[1]), 'reversed': 0, 'type': 0, 'min_speed': 0, 'max_speed': 0, 'lo': 'AND' } }, 'act': [{ 't': 'message', 'p': { 'name': 'Превысил время нахождения на маршруте %s' % geozone['name'], 'url': '', 'color': '', 'blink': 0 } }, { 't': 'event', 'p': { 'flags': 1 } }] } result = update_notification(data, sess_id) yield result[0], result[1], data
def space_notification(job, sess_id, routes_cache=None, job_template=None): """6. Нахождение объекта вне планового маршрута""" if not job_template or not job_template.space_overstatements_standard: raise NotificationError( 'Не найден норматив перенахождения вне маршрута. ID ПЛ=%s' % job.pk) if not routes_cache: routes = get_routes(sess_id, with_points=True) routes_cache = {r['id']: r for r in routes} dt_from = int(time.mktime(job.date_begin.timetuple())) dt_to = int(time.mktime(job.date_end.timetuple())) route = routes_cache.get(int(job.route_id), {}) route_title = route.get('name') geozones = route.get('points', []) geozones_ids = list(map(lambda x: x['id'].split('-')[1], geozones)) if not route or not geozones: raise NotificationError( 'Маршрут неизвестен или отсутствуют геозоны. ID ПЛ=%s' % job.pk) data = { 'itemId': get_wialon_report_resource_id(job.user, sess_id), 'id': 0, 'callMode': 'create', 'n': 'Нахождение вне планового маршрута %s' % route_title, 'txt': '%UNIT% находился вне планового маршрута ' + route_title, # время активации (UNIX формат) 'ta': dt_from, # время деактивации (UNIX формат) 'td': dt_to, # максимальное количество срабатываний (0 - не ограничено) 'ma': 0, # таймаут срабатывания(секунд) 'cdt': 10, # максимальный временной интервал между сообщениями (секунд) 'mmtd': 60 * 60, # минимальная продолжительность тревожного состояния (секунд) 'mast': int(job_template.space_overstatements_standard * 60.0), # минимальная продолжительность предыдущего состояния (секунд) 'mpst': 10, # период контроля относительно текущего времени (секунд) 'cp': 24 * 60 * 60, # флаги 'fl': 0, # часовой пояс 'tz': get_wialon_timezone_integer(job.user.timezone or get_current_timezone()), # язык пользователя (двухбуквенный код) 'la': 'ru', # массив ID объектов/групп объектов 'un': [int(job.unit_id)], 'sch': { 'f1': 0, 'f2': 0, 't1': 0, 't2': 0, 'm': 0, 'y': 0, 'w': 0 }, 'ctrl_sch': { 'f1': 0, 'f2': 0, 't1': 0, 't2': 0, 'm': 0, 'y': 0, 'w': 0 }, 'trg': { 't': 'geozone', 'p': { 'sensor_type': '', 'sensor_name_mask': '', 'lower_bound': 0, 'upper_bound': 0, 'merge': 0, 'geozone_ids': ','.join(geozones_ids), 'reversed': 0, 'type': 1, 'min_speed': 0, 'max_speed': 0, 'lo': 'AND' } }, 'act': [{ 't': 'message', 'p': { 'name': 'Нахождение вне планового маршрута %s' % route_title, 'url': '', 'color': '', 'blink': 0 } }, { 't': 'event', 'p': { 'flags': 1 } }] } result = update_notification(data, sess_id) yield result[0], result[1], data
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 cache_geozones(): print('Caching geozones...') with transaction.atomic(): i = 1 users = User.objects.filter(supervisor__isnull=False, wialon_username__isnull=False, wialon_password__isnull=False).exclude( wialon_username='', wialon_password='') print('%s users found' % len(users)) for user in users: sess_id = get_wialon_session_key(user) print('%s) User %s processing' % (i, user)) routes = get_routes(sess_id, with_points=True) logout_session(user, sess_id) del sess_id print('%s routes found' % len(routes)) for route in routes: print('Route %s' % route['name']) name = route['name'].strip() job_template, created = StandardJobTemplate.objects.get_or_create( wialon_id=str(route['id']), defaults={ 'title': name, 'user': user }) existing_points = set() if not created: if job_template.title != name: job_template.title = name job_template.user = user job_template.save() existing_points = {p for p in job_template.points.all()} # убираем дубли (когда одна и та же геозона дублируется в маршруте) route['points'] = {r['id']: r for r in route['points']}.values() print('%s points found, already exist: %s' % (len(route['points']), len(existing_points))) for point in route['points']: name = point['name'].strip() standard_point, created = StandardPoint.objects.get_or_create( job_template=job_template, wialon_id=str(point['id']), defaults={'title': name}) if not created: existing_points.discard(standard_point) if standard_point.title != name: standard_point.title = name standard_point.save() if existing_points: print('Points to remove: %s' % ', '.join([str(x) for x in existing_points])) for existing_point in existing_points: existing_point.delete() sleep(.3) i += 1
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 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(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(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(FinishedJobsView, self).get_context_data(**kwargs) report_data = None form = kwargs['form'] kwargs['today'] = datetime.date.today() stats = {'total': 0, 'non_actual': 0} if self.request.POST: report_data = OrderedDict() 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_list = get_routes(sess_id, with_points=True) routes_dict = { x['id']: x for x in routes_list if not is_fixed_route(x['name']) } all_routes_dict = {x['id']: x for x in routes_list} used_routes = set() stats['total'] = len(routes_dict) 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').order_by('date_begin', 'date_end') for job in jobs: route = routes_dict.get(int(job.route_id)) if not route: possible_route_name = all_routes_dict.get(int(job.route_id), {})\ .get('name', '') if not is_fixed_route(possible_route_name): print( 'Route not found (job_id: %s, route name: %s)' % (job.pk, possible_route_name)) continue key = int(job.route_id) used_routes.add(key) if key not in report_data: report_data[key] = self.get_new_grouping() report_data[key]['key'] = key report_data[key]['name'] = all_routes_dict.get( key, {}).get('name', '') report_data[key]['plan'] += 1 route_points = {p['name'] for p in route['points']} points = list( map( lambda x: x.title, filter(lambda x: x.title != 'SPACE', job.points.all()))) remaining_route_points = [ x for x in route_points if x not in points ] if remaining_route_points: stats['non_actual'] += 1 else: report_data[key]['finished'] += 1 for v in report_data.values(): v['ratio'] = round((v['finished'] / v['plan']) * 100, 2) # убираем полностью завершенные report_data = OrderedDict( (x[0], x[1]) for x in report_data.items() if x[1]['ratio'] < 100 - form.cleaned_data['non_actual_param']) # добавим те, которые вообще не использовались: unused_routes = [ x for x in routes_dict.values() if x['id'] not in used_routes ] for unused_route in unused_routes: key = unused_route['id'] report_data[key] = self.get_new_grouping(key) report_data[key]['name'] = all_routes_dict.get( key, {}).get('name', '') kwargs.update(stats=stats, report_data=report_data) return kwargs