Tests expressions DurationField

Classe Experiment

 1 class Experiment(models.Model):
 2     name = models.CharField(max_length=24)
 3     assigned = models.DateField()
 4     completed = models.DateField()
 5     estimated_time = models.DurationField()
 6     start = models.DateTimeField()
 7     end = models.DateTimeField()
 8
 9     class Meta:
10         db_table = 'expressions_ExPeRiMeNt'
11         ordering = ('name',)
12
13     def duration(self):
14         return self.end - self.start

Classe Time

1 class Time(models.Model):
2     time = models.TimeField(null=True)
3
4     def __str__(self):
5         return "%s" % self.time

class FTimeDeltaTests(TestCase)

  1 import datetime
  2 import pickle
  3 import unittest
  4 import uuid
  5 from copy import deepcopy
  6 from unittest import mock
  7
  8 from django.core.exceptions import FieldError
  9 from django.db import DatabaseError, connection
 10 from django.db.models import (
 11     Avg, BooleanField, Case, CharField, Count, DateField, DateTimeField,
 12     DurationField, Exists, Expression, ExpressionList, ExpressionWrapper, F,
 13     Func, IntegerField, Max, Min, Model, OrderBy, OuterRef, Q, StdDev,
 14     Subquery, Sum, TimeField, UUIDField, Value, Variance, When,
 15 )
 16 from django.db.models.expressions import Col, Combinable, Random, RawSQL, Ref
 17 from django.db.models.functions import (
 18     Coalesce, Concat, Length, Lower, Substr, Upper,
 19 )
 20 from django.db.models.sql import constants
 21 from django.db.models.sql.datastructures import Join
 22 from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature
 23 from django.test.utils import Approximate, isolate_apps
 24
 25 from .models import (
 26     UUID, UUIDPK, Company, Employee, Experiment, Number, RemoteEmployee,
 27     Result, SimulationRun, Time,
 28 )
 29
 30
 31 class FTimeDeltaTests(TestCase):
 32
 33     @classmethod
 34     def setUpTestData(cls):
 35         cls.sday = sday = datetime.date(2010, 6, 25)
 36         cls.stime = stime = datetime.datetime(2010, 6, 25, 12, 15, 30, 747000)
 37         midnight = datetime.time(0)
 38
 39         delta0 = datetime.timedelta(0)
 40         delta1 = datetime.timedelta(microseconds=253000)
 41         delta2 = datetime.timedelta(seconds=44)
 42         delta3 = datetime.timedelta(hours=21, minutes=8)
 43         delta4 = datetime.timedelta(days=10)
 44         delta5 = datetime.timedelta(days=90)
 45
 46         # Test data is set so that deltas and delays will be
 47         # strictly increasing.
 48         cls.deltas = []
 49         cls.delays = []
 50         cls.days_long = []
 51
 52         # e0: started same day as assigned, zero duration
 53         end = stime + delta0
 54         e0 = Experiment.objects.create(
 55             name='e0', assigned=sday, start=stime, end=end,
 56             completed=end.date(), estimated_time=delta0,
 57         )
 58         cls.deltas.append(delta0)
 59         cls.delays.append(e0.start - datetime.datetime.combine(e0.assigned, midnight))
 60         cls.days_long.append(e0.completed - e0.assigned)
 61
 62         # e1: started one day after assigned, tiny duration, data
 63         # set so that end time has no fractional seconds, which
 64         # tests an edge case on sqlite.
 65         delay = datetime.timedelta(1)
 66         end = stime + delay + delta1
 67         e1 = Experiment.objects.create(
 68             name='e1', assigned=sday, start=stime + delay, end=end,
 69             completed=end.date(), estimated_time=delta1,
 70         )
 71         cls.deltas.append(delta1)
 72         cls.delays.append(e1.start - datetime.datetime.combine(e1.assigned, midnight))
 73         cls.days_long.append(e1.completed - e1.assigned)
 74
 75         # e2: started three days after assigned, small duration
 76         end = stime + delta2
 77         e2 = Experiment.objects.create(
 78             name='e2', assigned=sday - datetime.timedelta(3), start=stime,
 79             end=end, completed=end.date(), estimated_time=datetime.timedelta(hours=1),
 80         )
 81         cls.deltas.append(delta2)
 82         cls.delays.append(e2.start - datetime.datetime.combine(e2.assigned, midnight))
 83         cls.days_long.append(e2.completed - e2.assigned)
 84
 85         # e3: started four days after assigned, medium duration
 86         delay = datetime.timedelta(4)
 87         end = stime + delay + delta3
 88         e3 = Experiment.objects.create(
 89             name='e3', assigned=sday, start=stime + delay, end=end,
 90             completed=end.date(), estimated_time=delta3,
 91         )
 92         cls.deltas.append(delta3)
 93         cls.delays.append(e3.start - datetime.datetime.combine(e3.assigned, midnight))
 94         cls.days_long.append(e3.completed - e3.assigned)
 95
 96         # e4: started 10 days after assignment, long duration
 97         end = stime + delta4
 98         e4 = Experiment.objects.create(
 99             name='e4', assigned=sday - datetime.timedelta(10), start=stime,
100             end=end, completed=end.date(), estimated_time=delta4 - datetime.timedelta(1),
101         )
102         cls.deltas.append(delta4)
103         cls.delays.append(e4.start - datetime.datetime.combine(e4.assigned, midnight))
104         cls.days_long.append(e4.completed - e4.assigned)
105
106         # e5: started a month after assignment, very long duration
107         delay = datetime.timedelta(30)
108         end = stime + delay + delta5
109         e5 = Experiment.objects.create(
110             name='e5', assigned=sday, start=stime + delay, end=end,
111             completed=end.date(), estimated_time=delta5,
112         )
113         cls.deltas.append(delta5)
114         cls.delays.append(e5.start - datetime.datetime.combine(e5.assigned, midnight))
115         cls.days_long.append(e5.completed - e5.assigned)
116
117         cls.expnames = [e.name for e in Experiment.objects.all()]

test_durationfield_add

 1 def test_durationfield_add(self):
 2     zeros = [e.name for e in Experiment.objects.filter(start=F('start') + F('estimated_time'))]
 3     self.assertEqual(zeros, ['e0'])
 4
 5     end_less = [e.name for e in Experiment.objects.filter(end__lt=F('start') + F('estimated_time'))]
 6     self.assertEqual(end_less, ['e2'])
 7
 8     delta_math = [
 9         e.name for e in
10         Experiment.objects.filter(end__gte=F('start') + F('estimated_time') + datetime.timedelta(hours=1))
11     ]
12     self.assertEqual(delta_math, ['e4'])
13
14     queryset = Experiment.objects.annotate(shifted=ExpressionWrapper(
15         F('start') + Value(None, output_field=DurationField()),
16         output_field=DateTimeField(),
17     ))
18     self.assertIsNone(queryset.first().shifted)

test_date_subtraction

 1 @skipUnlessDBFeature('supports_temporal_subtraction')
 2 def test_date_subtraction(self):
 3     queryset = Experiment.objects.annotate(
 4         completion_duration=ExpressionWrapper(
 5             F('completed') - F('assigned'), output_field=DurationField()
 6         )
 7     )
 8
 9     at_least_5_days = {e.name for e in queryset.filter(completion_duration__gte=datetime.timedelta(days=5))}
10     self.assertEqual(at_least_5_days, {'e3', 'e4', 'e5'})
11
12     at_least_120_days = {e.name for e in queryset.filter(completion_duration__gte=datetime.timedelta(days=120))}
13     self.assertEqual(at_least_120_days, {'e5'})
14
15     less_than_5_days = {e.name for e in queryset.filter(completion_duration__lt=datetime.timedelta(days=5))}
16     self.assertEqual(less_than_5_days, {'e0', 'e1', 'e2'})
17
18     queryset = Experiment.objects.annotate(difference=ExpressionWrapper(
19         F('completed') - Value(None, output_field=DateField()),
20         output_field=DurationField(),
21     ))
22     self.assertIsNone(queryset.first().difference)
23
24     queryset = Experiment.objects.annotate(shifted=ExpressionWrapper(
25         F('completed') - Value(None, output_field=DurationField()),
26         output_field=DateField(),
27     ))
28     self.assertIsNone(queryset.first().shifted)

test_date_subquery_subtraction

1 @skipUnlessDBFeature('supports_temporal_subtraction')
2 def test_date_subquery_subtraction(self):
3     subquery = Experiment.objects.filter(pk=OuterRef('pk')).values('completed')
4     queryset = Experiment.objects.annotate(
5         difference=ExpressionWrapper(
6             subquery - F('completed'), output_field=DurationField(),
7         ),
8     ).filter(difference=datetime.timedelta())
9     self.assertTrue(queryset.exists())

test_time_subquery_subtraction

 1 @skipUnlessDBFeature('supports_temporal_subtraction')
 2 def test_time_subquery_subtraction(self):
 3     Time.objects.create(time=datetime.time(12, 30, 15, 2345))
 4     subquery = Time.objects.filter(pk=OuterRef('pk')).values('time')
 5     queryset = Time.objects.annotate(
 6         difference=ExpressionWrapper(
 7             subquery - F('time'), output_field=DurationField(),
 8         ),
 9     ).filter(difference=datetime.timedelta())
10     self.assertTrue(queryset.exists())

test_datetime_subtraction_microseconds

1 @skipUnlessDBFeature('supports_temporal_subtraction')
2 def test_datetime_subtraction_microseconds(self):
3     delta = datetime.timedelta(microseconds=8999999999999999)
4     Experiment.objects.update(end=F('start') + delta)
5     qs = Experiment.objects.annotate(
6         delta=ExpressionWrapper(F('end') - F('start'), output_field=DurationField())
7     )
8     for e in qs:
9         self.assertEqual(e.delta, delta)

test_duration_with_datetime

1 def test_duration_with_datetime(self):
2     # Exclude e1 which has very high precision so we can test this on all
3     # backends regardless of whether or not it supports
4     # microsecond_precision.
5     over_estimate = Experiment.objects.exclude(name='e1').filter(
6         completed__gt=self.stime + F('estimated_time'),
7     ).order_by('name')
8     self.assertQuerysetEqual(over_estimate, ['e3', 'e4', 'e5'], lambda e: e.name)

test_duration_with_datetime_microseconds

1 def test_duration_with_datetime_microseconds(self):
2     delta = datetime.timedelta(microseconds=8999999999999999)
3     qs = Experiment.objects.annotate(dt=ExpressionWrapper(
4         F('start') + delta,
5         output_field=DateTimeField(),
6     ))
7     for e in qs:
8         self.assertEqual(e.dt, e.start + delta)

test_date_minus_duration

1 def test_date_minus_duration(self):
2     more_than_4_days = Experiment.objects.filter(
3         assigned__lt=F('completed') - Value(datetime.timedelta(days=4), output_field=DurationField())
4     )
5     self.assertQuerysetEqual(more_than_4_days, ['e3', 'e4', 'e5'], lambda e: e.name)

test_negative_timedelta_update

 1 def test_negative_timedelta_update(self):
 2     # subtract 30 seconds, 30 minutes, 2 hours and 2 days
 3     experiments = Experiment.objects.filter(name='e0').annotate(
 4         start_sub_seconds=F('start') + datetime.timedelta(seconds=-30),
 5     ).annotate(
 6         start_sub_minutes=F('start_sub_seconds') + datetime.timedelta(minutes=-30),
 7     ).annotate(
 8         start_sub_hours=F('start_sub_minutes') + datetime.timedelta(hours=-2),
 9     ).annotate(
10         new_start=F('start_sub_hours') + datetime.timedelta(days=-2),
11     )
12     expected_start = datetime.datetime(2010, 6, 23, 9, 45, 0)
13     # subtract 30 microseconds
14     experiments = experiments.annotate(new_start=F('new_start') + datetime.timedelta(microseconds=-30))
15     expected_start += datetime.timedelta(microseconds=+746970)
16     experiments.update(start=F('new_start'))
17     e0 = Experiment.objects.get(name='e0')
18     self.assertEqual(e0.start, expected_start)