class PostConfigFieldMixin: camera_config_service = obj_graph().provide(CameraConfigService) def post_config_field(self, camera_id: str, config_field: SingleConfigForm): form = config_field if len(form.visible_fields()) == 0 or form.field.disabled: return if not (form.is_valid() and form.has_changed()): return try: self.camera_config_service.set_config( camera_id=camera_id, config_name=form.name, config_value=form.cleaned_data.get(form.name)) form.tried_to_change = True # Check if it actually changed field_config = self.camera_config_service.get_config(camera_id=camera_id, config_name=form.name) form.managed_to_change = (field_config.value == form.cleaned_data[form.name]) # Set the current value if it didn't change if not form.managed_to_change: form.set_value(field_config.value) except CameraException as e: form.add_error(form.name, str(e))
class CameraPreviewSource(View): camera_ctrl_service = obj_graph().provide(CameraCtrlService) def get(self, request, *args, **kwargs): camera_id = kwargs['camera_id'] return StreamingHttpResponse( self._preview_generator(camera_id=camera_id), content_type="multipart/x-mixed-replace;boundary=frame") def _preview_generator(self, camera_id): try: while True: frame = self\ .camera_ctrl_service\ .camera_capture_preview(camera_id=camera_id) yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + frame.tobytes() + b'\r\n') except CameraNotFoundException: return CameraNotFoundApiException(camera_id=camera_id) except CameraException as e: return HttpResponseServerError(content=str(e))
class AllConfigs(TemplateView, PostConfigFieldMixin, ErrorUtilsMixin): template_name = 'camera_config/all_configs.html' camera_config_service = obj_graph().provide(CameraConfigService) def dispatch(self, request, *args, **kwargs): context = self.get_context_data(**kwargs) camera_id = kwargs['camera_id'] context['camera_id'] = camera_id try: configs_dto = self.camera_config_service.get_all_configs( camera_id=camera_id) config_form_manager = CameraConfigFormManager(request.POST or None) config_form_manager.load_from_configs_dto(configs=configs_dto) if request.method == 'POST': self.post(camera_id=camera_id, config_form_manager=config_form_manager) except CameraException as e: return self.render_to_error(request=request, error=str(e)) context['form'] = config_form_manager return self.render_to_response(context=context) def post(self, camera_id: str, config_form_manager: CameraConfigFormManager): for section in config_form_manager.sections: for field in section.form_fields: self.post_config_field(camera_id=camera_id, config_field=field)
class CronScheduleUpdate(UpdateView): model = CronSchedule template_name = 'scheduling/cron/cron_schedule_create.html' success_message = 'Schedule successfully updated' fields = [ 'name', 'start_date', 'end_date', 'year', 'month', 'day', 'week', 'day_of_week', 'hour', 'minute', 'second' ] scheduler = obj_graph().provide(_DiScheduler).scheduler def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['updating'] = True return context def form_valid(self, form): response = super().form_valid(form) schedule = cron_schedule.CronSchedule(**vars(self.object)) hard_map_objects(self.object, schedule) try: self.scheduler.modify(schedule) except Exception as e: form.add_error(None, str(e)) return super().form_invalid(form) messages.success(self.request, self.success_message) return response
class FavFieldsListUpdate(TemplateView, ErrorUtilsMixin): template_name = 'app_settings/fav_fields_settings.html' favourite_configs_service: FavouriteConfigsService = obj_graph().provide(FavouriteConfigsService) def dispatch(self, request, *args, **kwargs): context = self.get_context_data(**kwargs) fav_field_formset = list(self._create_formset(request=request)) if request.method == 'POST': self.post(formset=fav_field_formset) context['formset'] = fav_field_formset return self.render_to_response(context=context) def post(self, formset: Iterable[FavouriteConfigForm]): for form in formset: if not (form.is_valid() and form.has_changed()): continue self.favourite_configs_service.update(pk=form.cleaned_data['pk'], config_name=form.cleaned_data['name']) def _create_formset(self, request): fav_fields = list(self.favourite_configs_service.get_all()) for fav_field in fav_fields: initial = {'pk': fav_field.pk, 'name': fav_field.name} form = FavouriteConfigForm(request.POST or None, initial=initial, prefix='field_{}'.format(fav_field.pk)) form.model_pk = fav_field.pk yield form
class TimelapseCreate(CreateView): template_name = 'scheduling/timelapse/timelapse_create.html' model = Timelapse fields = ['name', 'storage_dir_format', 'filename_format', 'schedule'] success_message = 'Successfully created timelapse' schedule_service = obj_graph().provide(ScheduleService) def form_valid(self, form): response = super().form_valid(form) assert isinstance(self.object, Timelapse) schedule = CronSchedule.objects.filter(pk=self.object.schedule_id) if len(schedule) != 0: schedule = schedule[0] try: self.do_the_schedule_thing(schedule) except Exception as e: form.add_error(None, str(e)) return super().form_invalid(form) messages.success(request=self.request, message=self.success_message) return response def do_the_schedule_thing(self, schedule: CronSchedule): dto = cron_schedule.CronSchedule(**vars(schedule)) dto.pk = schedule.pk job_id = self.schedule_service.run_timelapse(self.object.pk, dto) self.object.schedule_job_id = job_id self.object.save()
class SingleConfig(TemplateView, PostConfigFieldMixin, ErrorUtilsMixin): template_name = 'camera_config/single_config.html' camera_config_service = obj_graph().provide(CameraConfigService) def dispatch(self, request, *args, **kwargs): context = self.get_context_data(**kwargs) camera_id = kwargs['camera_id'] context['camera_id'] = camera_id config_name = kwargs['config_name'] try: config = self.camera_config_service.get_config( camera_id=camera_id, config_name=config_name) form = SingleConfigForm.from_config_dto(config_dto=config, post_data=request.POST or None) context['form'] = form if request.method == 'POST': self.post_config_field(camera_id=camera_id, config_field=form) except CameraException as e: return self.render_to_error(request=request, error=str(e)) return self.render_to_response(context=context)
class FavFieldAdd(View): default_dummy_value = 'shutterspeed' favourite_configs_service = obj_graph().provide(FavouriteConfigsService) def get(self, request, *args, **kwargs): self.favourite_configs_service.add( config_name=self.default_dummy_value) return HttpResponse(status=201)
class FavFieldRemove(DeleteView): favourite_configs_service = obj_graph().provide(FavouriteConfigsService) def delete(self, request, *args, **kwargs): fav_field_pk = kwargs['pk'] self.favourite_configs_service.remove(pk=int(fav_field_pk)) return HttpResponse(status=200)
class MultiPreview(TemplateView): template_name = 'camera_ctrl/multi_preview.html' camera_ctrl_service = obj_graph().provide(CameraCtrlService) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) cameras = self.camera_ctrl_service.cameras_get_all() context['cameras'] = list(map_cameras_to_view_models(cameras)) return context
class CamerasAutodetect(View): camera_ctrl_service = obj_graph().provide(CameraCtrlService) def get(self, request): try: self.camera_ctrl_service.cameras_autodetect() return HttpResponse(status=200) except CameraException as e: return HttpResponseServerError(content=str(e))
class CameraRemove(View): camera_ctrl_service = obj_graph().provide(CameraCtrlService) def get(self, request, *args, **kwargs): camera_id = kwargs['camera_id'] try: self.camera_ctrl_service.camera_remove(camera_id=camera_id) return HttpResponse(status=204) except CameraNotFoundException: return CameraNotFoundApiException(camera_id=camera_id)
class SinglePreview(TemplateView, FavConfigsMixin, ErrorUtilsMixin): template_name = 'camera_ctrl/single_preview.html' camera_ctrl_service = obj_graph().provide(CameraCtrlService) camera_config_service = obj_graph().provide(CameraConfigService) def dispatch(self, request, *args, **kwargs): context = super().get_context_data(**kwargs) camera_id = kwargs['camera_id'] try: camera = self.camera_ctrl_service.camera_get(camera_id=camera_id) context['camera'] = map_camera_to_view_model(camera) context['form'] = self.fav_configs_dispatch(request=request, camera_id=camera_id) except CameraException as e: return self.render_to_error(request=request, error=str(e)) return self.render_to_response(context=context)
class CamerasHardResetAll(View): camera_ctrl_service = obj_graph().provide(CameraCtrlService) def get(self, request): try: self.camera_ctrl_service.hard_reset_all_cameras() return HttpResponse(status=200) except CameraResetException as e: return HttpResponseServerError(content=str(e)) except CameraException as e: return HttpResponseServerError(content=str(e))
class CronScheduleDelete(DeleteView): model = CronSchedule scheduler = obj_graph().provide(_DiScheduler).scheduler def get(self, request, *args, **kwargs): try: obj = self.get_object() self.scheduler.delete(obj.pk) obj.delete() except Exception as e: return HttpResponseServerError(content=str(e)) return HttpResponse(status=200)
class CameraReconnect(View): camera_ctrl_service = obj_graph().provide(CameraCtrlService) def get(self, request, *args, **kwargs): camera_id = kwargs['camera_id'] try: self.camera_ctrl_service.camera_reconnect(camera_id=camera_id) return HttpResponse(status=200) except CameraNotFoundException: return CameraNotFoundApiException(camera_id=camera_id) except CameraException as e: return HttpResponseServerError(content=str(e))
class ScheduledConfigCreate(CreateView): model = ScheduledConfig fields = ['name', 'schedule'] template_name = 'scheduling/scheduled_config/scheduled_config_create.html' schedule_service = obj_graph().provide(ScheduleService) def get_context_data(self, **kwargs): # we need to overwrite get_context_data # to make sure that our formset is rendered data = super().get_context_data(**kwargs) if self.request.POST: data['fields'] = ScheduledConfigFieldFormSet(self.request.POST) else: data['fields'] = ScheduledConfigFieldFormSet() return data def form_valid(self, form): context = self.get_context_data() fields = context['fields'] self.object = form.save() if fields.is_valid(): fields.instance = self.object fields.save() else: return super().form_invalid(form) response = super().form_valid(form) schedules = CronSchedule.objects.filter(pk=self.object.schedule_id) if len(schedules) != 0: schedule = schedules[0] try: self.do_the_schedule_thing(schedule) except Exception as e: form.add_error(None, str(e)) return super().form_invalid(form) return response def do_the_schedule_thing(self, schedule: CronSchedule): dto = cron_schedule.CronSchedule(**vars(schedule)) dto.pk = schedule.pk job_id = self.schedule_service.run_scheduled_config( self.object.pk, dto) self.object.schedule_job_id = job_id self.object.save()
class FavConfigsMixin(PostConfigFieldMixin): camera_config_service = obj_graph().provide(CameraConfigService) def fav_configs_dispatch(self, request, camera_id: str): fav_configs = self.camera_config_service.get_favourite_configs(camera_id=camera_id) fav_configs_section = ConfigFormSection( name='favourite', label='Favourite', post_data=request.POST or None) fav_configs_section.add_fields(fields=fav_configs) if request.method == 'POST': for field in fav_configs_section.form_fields: self.post_config_field(camera_id=camera_id, config_field=field) return fav_configs_section
class TimelapseUpdate(UpdateView): template_name = 'scheduling/timelapse/timelapse_create.html' model = Timelapse fields = ['name', 'storage_dir_format', 'filename_format', 'schedule'] success_message = 'Timelapse successfully updated' schedule_service = obj_graph().provide(ScheduleService) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['updating'] = True return context def form_valid(self, form): previous_job_id = self.object.schedule_job_id response = super().form_valid(form) assert isinstance(self.object, Timelapse) try: if previous_job_id: self.schedule_service.delete_job(job_id=previous_job_id) self.object.schedule_job_id = None self.object.save() self.set_new_schedule_job() except Exception as e: form.add_error(None, str(e)) return super().form_invalid(form) messages.success(request=self.request, message=self.success_message) return response def set_new_schedule_job(self): schedule = CronSchedule.objects.filter(pk=self.object.schedule_id) if len(schedule) == 0: return schedule = schedule[0] dto = cron_schedule.CronSchedule(**vars(schedule)) dto.pk = schedule.pk job_id = self.schedule_service.run_timelapse(self.object.pk, dto) self.object.schedule_job_id = job_id self.object.save()
class ConfigList(TemplateView, ErrorUtilsMixin): template_name = 'camera_config/config_list.html' camera_config_service = obj_graph().provide(CameraConfigService) def dispatch(self, request, *args, **kwargs): context = self.get_context_data(**kwargs) camera_id = kwargs['camera_id'] context['camera_id'] = camera_id try: config_names = self.camera_config_service.get_all_config_names( camera_id=camera_id) context['config_names'] = list(config_names) except CameraException as e: return self.render_to_error(request=request, error=str(e)) return self.render_to_response(context=context)
class CameraCaptureImgAndDownload(View): camera_ctrl_service = obj_graph().provide(CameraCtrlService) def get(self, request, *args, **kwargs): camera_id = kwargs['camera_id'] try: capture_dto = self\ .camera_ctrl_service\ .camera_capture_img_and_download(camera_id=camera_id) return FileResponse( open(capture_dto.real_file_path, 'rb'), as_attachment=True, filename=capture_dto.download_filename) except CameraNotFoundException: return CameraNotFoundApiException(camera_id=camera_id) except CameraException as e: return HttpResponseServerError(content=str(e))
def startup_factory() -> WebStartup: return obj_graph().provide(WebStartup)
class CameraConfigsView(APIView): camera_ctrl_service = obj_graph().provide(CameraCtrlService) camera_config_service = obj_graph().provide(CameraConfigService) def get(self, request, *args, **kwargs): camera_id = kwargs['camera_id'] config_name = kwargs['config_name'] try: if camera_id.lower() == 'first': camera = self.camera_ctrl_service.cameras_get_first() if camera is None: raise CameraNotFoundException() camera_id = camera.id config = self.camera_config_service.get_config( camera_id=camera_id, config_name=config_name) return HttpResponse(content=config.value) except CameraNotFoundException: return CameraNotFoundApiException(camera_id=camera_id) except InvalidConfigException as e: return HttpResponseBadRequest(content=str(e)) except CameraException as e: return HttpResponseServerError(content=str(e)) def put(self, request, *args, **kwargs): camera_id = kwargs['camera_id'] config_name = kwargs['config_name'] config_value = request.GET.get('value') try: if camera_id.lower() == 'first': camera = self.camera_ctrl_service.cameras_get_first() if camera is None: raise CameraNotFoundException() camera_id = camera.id self.camera_config_service.set_config(camera_id=camera_id, config_name=config_name, config_value=config_value) current_config = self.camera_config_service.get_config( camera_id=camera_id, config_name=config_name) managed_to_change = (str(current_config.value) == config_value) if not managed_to_change: error = f'Failed to change {config_name} to {config_value}' return HttpResponseBadRequest(content=error) return HttpResponse( content=f'Changed {config_name} to {config_value}') except CameraNotFoundException: return CameraNotFoundApiException(camera_id=camera_id) except InvalidConfigException as e: return HttpResponseBadRequest(content=str(e)) except CameraException as e: return HttpResponseServerError(content=str(e))
class ScheduledConfigUpdate(UpdateView): model = ScheduledConfig schedule_service = obj_graph().provide(ScheduleService) success_message = 'Scheduled Config successfully updated' template_name = 'scheduling/scheduled_config/scheduled_config_create.html' fields = ['name', 'schedule'] def get_context_data(self, **kwargs): scheduled_config = ScheduledConfig.objects.get(id=self.kwargs['pk']) context = super().get_context_data(**kwargs) context['updating'] = True if self.request.POST: context['fields'] = ScheduledConfigFieldFormSet( self.request.POST, instance=scheduled_config) else: context['fields'] = ScheduledConfigFieldFormSet( instance=scheduled_config) return context def form_valid(self, form): previous_job_id = self.object.schedule_job_id context = self.get_context_data() fields = context['fields'] self.object = form.save() if fields.is_valid(): fields.instance = self.object fields.save() else: return super().form_invalid(form) response = super().form_valid(form) try: if previous_job_id: self.schedule_service.delete_job(job_id=previous_job_id) self.object.schedule_job_id = None self.object.save() self._reschedule_job() except Exception as e: form.add_error(None, str(e)) return super().form_invalid(form) messages.success(request=self.request, message=self.success_message) return response def _reschedule_job(self): schedule = CronSchedule.objects.filter(pk=self.object.schedule_id) if len(schedule) == 0: return schedule = schedule[0] dto = cron_schedule.CronSchedule(**vars(schedule)) dto.pk = schedule.pk job_id = self.schedule_service.run_scheduled_config( self.object.pk, dto) self.object.schedule_job_id = job_id self.object.save()