def func_wrapper(*args, **kwargs): if settings.TEST or settings.SHELL: # Tests and the shell should just run immediately and not catch any # errors. return func(*args, **kwargs) elif settings.DEBUG or settings.BACKGROUND: command_name = func.__name__ command_run = LoggedCommand.objects.create( command_name=command_name, started=now()) results = None try: results = func(*args, **kwargs) command_run.completed = now() except Exception: exc_info = sys.exc_info() command_run.error = log_error( '{} error'.format(command_name), exc_info=exc_info) if settings.DEBUG: type_, value_, traceback_ = exc_info print(value_.__repr__()) print('\n'.join(traceback.format_tb(traceback_))) command_run.save() return results else: # Otherwise we're on web or something and we wanna kick this function # off to a background thread. q.enqueue(func, *args, **kwargs)
def logged_existing_error(self, title): existing = self.filter(title=title, created__gte=now() - timedelta(days=1), fixed__isnull=True).first() if existing: existing.count += 1 existing.latest = now() existing.save() if settings.DEBUG: print('Incrementing "{}" error count'.format(title)) return existing
def use_token_and_return_user(token_str, nnow=None): if not token_str: return if isinstance(token_str, LoginToken): raise Exception( 'You are supposed to hand the token string to this function, not the ' 'token object') nnow = nnow or now() existing = LoginToken.objects.filter(token=token_str, used__isnull=True).first() if existing: existing.used = nnow existing.save() if existing.user: return existing.user elif existing.email: # This just makes sign up links work as log in links, which is especially # handy for developing the web demo. existing_user = User.objects.filter(email=existing.email).first() if existing_user: return existing_user return User.objects.create(email=existing.email, username=existing.email) raise Exception('LoginToken {} has neither user nor email'.format( existing.pk))
def as_html(self, nnow=None): nnow = nnow or now() today = nnow.date() days = [] day_delta_and_css_classes = [(-2, 'two_before'), (-1, 'one_before'), (0, ''), (1, 'one_after'), (2, 'two_after')] for day_delta, css_class in day_delta_and_css_classes: this_day = self.on_day + timedelta(days=day_delta) if self.min_day and this_day < self.min_day: continue if self.max_day and this_day > self.max_day: continue is_current = this_day == self.on_day text = days_ago_str(this_day) if this_day == today: url = reverse('enter_time') else: url = url_with_date(reverse('enter_time'), day=this_day) days.append( RollerDay(is_current, text, css_class, url, date_to_url_str(this_day))) try: return render_to_string('date_roller.html', {'days': days}) except TemplateDoesNotExist: raise Exception( 'In order to use the date roller you need to add djaveNav/templates ' 'to the DIRS attribute in the TEMPLATES setting')
def log_long_request(self, path, duration, method, variables, nnow=None): nnow = nnow or now() existing = self.filter(path=path, method=method, created__gte=now() - timedelta(days=1)).first() if existing: existing.total_duration += duration existing.latest = now() existing.count += 1 existing.save() return existing return self.create(path=path, total_duration=duration, method=method, variables=variables, latest=now(), count=1)
def ajax_delete(request): instance = get_request_ajax_obj(request) if hasattr(instance, 'deleted'): instance.deleted = now() instance.save() else: instance.delete()
def send(self, _post=None, nnow=None): _post = _post or post nnow = nnow or now() if _post(self.webhook_url, json.loads(self.payload)): self.success_at = nnow self.last_send_attempt_at = nnow self.attempts += 1 self.save()
def __init__(self, request_get, nnow=None): nnow = nnow or now() default_to_date = nnow.date() self.status_filter = get_fixable_status_filter() filters = [self.status_filter] super().__init__( default_from_date(nnow=nnow), default_to_date, request_get=request_get, filters=filters) self.set_contents(self.get_table())
def resize_all(nnow=None, **kwargs): """ If all we ever get is a SignedFile, then the image will never be resized. """ nnow = nnow or now() photos = Photo.objects.filter( ~Q(file_name=''), # Sometimes a single empty file name is in the db resized_at__isnull=True, created__gte=nnow - timedelta(days=7)) for photo in photos: photo.as_child_class().resize(**kwargs)
def notify(self, to_emails, title, message, email_sender=None): """ Log to_emails, title, and message. If it's the first we've seen it today, send an email as well. """ email_sender = email_sender or StaffEmailSender() if isinstance(to_emails, str): to_emails = [to_emails] # I had days=1, but what if you want to send a daily notification? Monday # it goes out at 3:01am, Tuesday it tries to go out at 3:00am but it # doesn't because it went out within the last day. existing = self.filter( to=str(to_emails), title=title, message=message, created__gte=now() - timedelta(hours=23)).first() if existing: existing.count += 1 existing.latest = now() existing.save() else: self.create( to=str(to_emails), title=title, message=message, count=1, latest=now()) email_sender.send_mail( title, message, settings.SERVER_EMAIL, to_emails)
def _date_or_date_time(field, raw_value): if raw_value: if field.type == DATE: value = parse_date(raw_value) example = date.today().isoformat() elif field.type == DATE_TIME: value = parse_datetime(raw_value) example = now().isoformat() else: raise Exception(field.type) if not value: raise Problem( ('{} is not a valid {}. I am looking for something more ' 'like {}').format(raw_value, field.type, example)) return value
def get_fixables(model, from_date=USE_DEFAULT, to_date=USE_DEFAULT, status=USE_DEFAULT, nnow=None): """ Assumes you have a created column and a fixed column. Easiest thing is to inherit from djavError.models.fixable I'm leaving this here instead of in djavError.models.fixable because this function knows about UI specific filters and defaults. """ nnow = nnow or now() if from_date == USE_DEFAULT: from_date = default_from_date(nnow=nnow) if to_date == USE_DEFAULT: to_date = nnow.date() query_set = model.objects.filter( created__gte=beginning_of_day(from_date), created__lte=end_of_day(to_date)).order_by('-created') return do_fixed_filter(query_set, status)
def _js_variables_to_py(model, request_post): field_defs = model_fields_lookup(model) to_return = {} for key in request_post: if key not in ['pk', 'type', 'csrfmiddlewaretoken']: field = field_defs.get(key, None) if not field: raise Problem('{} is not a field in {}'.format( key, model.__class__.__name__)) DATE_TIME if field.type == BOOLEAN and request_post[key] in [ 'true', 'false', 'True', 'False']: to_return[key] = request_post[key].lower() == 'true' elif field.type == DATE_TIME and request_post[key] in [ 'now', 'Now']: to_return[key] = now().isoformat() else: to_return[key] = request_post[key] return to_return
def clean_up_never_used(nnow=None, bucket=None): """ I find out about every file that users upload because the server has to sign them. But WHY they're being used doesn't become clear until somebody creates a File object, and then the child class explains everything. So whatever SignedFiles exist without File objects are files that got uploaded, but we never found out why. So just delete them. This is quite common. People might change their minds and upload a different file, or they may just never submit the form that says why they uploaded that file. Once we know whether or not a file is used, we no longer need the SignedFile. """ nnow = nnow or now() # It may take a moment for the background thread to catch up and use a file, # or it may take the user a while to submit the form that says how to use the # file they just uploaded. So give it a day for the reason for the upload to # show up, and if it doesn't, chuck the upload. for signed in SignedFile.objects.filter(created__lte=nnow - timedelta(days=1)).order_by('pk'): if not signed_file_is_used(signed): signed.delete_file(bucket=bucket) signed.delete()
def create_error(self, title, message, exc_info=None, extra_to=None, supress_stack_trace=False): # Just in case somebody passes a tuple in or whatever as the message. message = str(message) value_ = None traceback_ = None if exc_info: type_, value_, traceback_ = exc_info stack_trace = '' if supress_stack_trace: stack_trace = '' elif traceback_: stack_trace = '\n'.join(traceback.format_tb(traceback_)) else: stack_trace = '\n'.join( [line.strip() for line in traceback.format_stack()]) error_message = '' if value_: error_message = value_.__repr__() to_emails = [settings.EMAIL_ERRORS_TO] if extra_to: to_emails.extend(extra_to) return self.create(title=title, count=1, latest=now(), message=message, error_message=error_message, stack_trace=stack_trace, to=','.join(to_emails))
def clean_up_no_longer_needed(nnow=None, bucket=None): """ Files have to describe when they can be thrown out. bucket is just used in tests to inject dependencies, but in case anybody actually tries to just clean_up_no_longer_needed in a specific bucket, I only deal with files in bucket if provided. """ nnow = nnow or now() # Sometimes a single empty file name slips into the database. There's a # unique constraint, so only 1 empty file_name is allowed haha Anyway # obviously you can't delete a file with no name. for file in File.objects.filter(~Q(file_name=''), keep_until__lt=nnow): file = file.as_child_class() if bucket and bucket.name() != file.bucket_config().name: continue if file.explain_why_can_delete(): file.delete(bucket=bucket) else: file.calc_and_set_keep(nnow=nnow) if file.keep_until >= nnow: file.save() else: raise Exception( 'Theres no reason to keep {} but calc_and_set_keep set ' 'keep_until to {} which is in the past'.format( file, file.keep_until))
def save(self, *args, **kwargs): if not self.latest: self.latest = now() super().save(*args, **kwargs)
def increment(self): self.count += 1 self.latest = now() self.save()
def resize(self, verbose=False, image_opener=None, use_os=None, use_open=None, **kwargs): """ This is a whole thing, and should run only in the background. It tries to run right away, but it also runs every night at midnight to help catch problems, so there are races possible, such as both threads trying to delete the working images. """ if self.resized_at: return True image_opener = image_opener or Image.open use_os = use_os or os use_open = use_open or open bucket = kwargs.get('bucket', None) or self._bucket() if self.__class__ in (File, Photo): raise Exception('Call resize on the child class') if self.resized_at: return if not self.file_name: raise Exception('This photo was deleted and can\'t be recovered.') bucket.download(self.file_name) try: image = image_opener(self.file_name) except OSError as ex: if ex.args[0].find('cannot identify image file') == 0: self.notify_bad_image(**kwargs) return False raise ex # Sometimes people will upload pngs. Sometimes they'll upload pngs but # replace the file extension with jpg. Whatever, people are dumb at image # file formats. In any case just make sure it doesn't have an alpha channel # so we can save it as a jpg, which is pretty much the sane format for a # photograph. try: image = image.convert('RGB') except OSError as ex: if ex.args[0].find('image file is truncated') == 0: self.notify_bad_image(**kwargs) return False raise ex (width, height) = (image.width, image.height) max_dimension = max((width, height)) if max_dimension > self.max_width_or_height(): ratio = self.max_width_or_height() / max_dimension image = image.resize((int(ratio * width), int(ratio * height))) if use_os.path.exists(self.file_name): use_os.remove(self.file_name) # I don't use the handy `with ... as` because I didn't bother to figure out # how to mock that whole thing in tests. file_handle = use_open(self.file_name, 'wb') image.save(file_handle, format='JPEG', quality=100) file_handle.close() self.do_additional_resize_steps(self.file_name, self.file_name, **kwargs) bucket.upload(self.file_name) use_os.remove(self.file_name) self.resized_at = kwargs.get('nnow', now()) self.save() self.post_resize() return True
def all_fixed(request, model_name, pks): model = model_from_name(model_name, subclass_of=Fixable) pks = [int(id) for id in pks.split(',')] model.allowed_by_user(request.user).filter(pk__in=pks).update(fixed=now())
def mark_deleted(self, nnow=None): nnow = nnow or now() self.deleted = nnow self.save()
def set_fixable_stuff(fixable, **kwargs): fixable.count = kwargs.get('count', 1) fixable.latest = kwargs.get('latest', now()) fixable.fixed = kwargs.get('fixed', None) fixable.save() return fixable
def default_from_date(nnow=None): nnow = nnow or now() return nnow.date() - timedelta(days=7)