File: unique_random_fixture.py

package info (click to toggle)
python-django-dynamic-fixture 4.0.1-1~bpo12%2B1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-backports
  • size: 616 kB
  • sloc: python: 3,909; makefile: 237; sh: 6
file content (173 lines) | stat: -rw-r--r-- 5,799 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
from datetime import datetime, date, timedelta
from decimal import Decimal
from itertools import chain
import random
import socket
import string
import struct
from warnings import warn

from django.core.exceptions import ImproperlyConfigured

try:
    from django.utils.timezone import now
except ImportError:
    now = datetime.now

try:
    from django.contrib.gis.geos import *
except ImproperlyConfigured:
    pass  # environment without geo libs
except Exception:
    pass # Avoid errors like GDALException

from django_dynamic_fixture.fixture_algorithms.sequential_fixture import AutoDataFiller
from django_dynamic_fixture.fixture_algorithms.default_fixture import BaseDataFixture, GeoDjangoFixtureMixin, PostgresFixtureMixin


class UniqueRandomDataFixture(BaseDataFixture, GeoDjangoFixtureMixin, PostgresFixtureMixin):
    DEFAULT_LENGTH = 10
    OBJECT_COUNT = 512
    WARNING_MESSAGE_TMPL = (
        'Maximum number of objects (%d) is exceeded in '
        'unique_random_fixture. Uniqueness is not guaranteed.'
    )

    def __init__(self):
        super().__init__()
        self.filler = AutoDataFiller()

    def get_counter(self, field, key):
        result = self.filler.next(key)
        if result > self.OBJECT_COUNT:
            warn(self.WARNING_MESSAGE_TMPL % self.OBJECT_COUNT, RuntimeWarning)
        return result

    def random_string(self, field, key, n=None):
        counter = str(self.get_counter(field, key))
        length = n or self.DEFAULT_LENGTH
        result = counter
        result += ''.join(
            random.choice(string.ascii_letters)
            for _ in range(length - len(counter))
        )
        return result

    def random_integer(self, field, key, signed=True):
        counter = self.get_counter(field, key) - 1
        counter %= self.OBJECT_COUNT
        if not signed:
            MAX_INT = 2 ** 16
            multiplier = MAX_INT // self.OBJECT_COUNT
            return random.randrange(
                multiplier * counter + 1, multiplier * (counter + 1)
            )

        MAX_SIGNED_INT = 2 ** 15
        multiplier = MAX_SIGNED_INT // self.OBJECT_COUNT
        positive_range = range(
            multiplier * counter + 1, multiplier * (counter + 1)
        )
        negative_range = range(
            (-multiplier) * (counter + 1), (-multiplier) * counter
        )
        return random.choice(list(chain(positive_range, negative_range)))

    # NUMBERS
    def integerfield_config(self, field, key):
        return self.random_integer(field, key)

    def smallintegerfield_config(self, field, key):
        return self.random_integer(field, key)

    def bigintegerfield_config(self, field, key):
        return self.random_integer(field, key)

    def positiveintegerfield_config(self, field, key):
        return self.random_integer(field, key, signed=False)

    def positivesmallintegerfield_config(self, field, key):
        return self.random_integer(field, key, signed=False)

    def floatfield_config(self, field, key):
        return float(self.random_integer(field, key)) + random.random()

    def decimalfield_config(self, field, key):
        number_of_digits = field.max_digits - field.decimal_places
        max_value = 10 ** number_of_digits
        value = self.random_integer(field, key) % max_value
        value = float(value) + random.random()
        return Decimal(str(value))

    # STRINGS
    def charfield_config(self, field, key):
        return self.random_string(field, key, field.max_length)

    def textfield_config(self, field, key):
        return self.charfield_config(field, key)

    def slugfield_config(self, field, key):
        return self.charfield_config(field, key)

    def commaseparatedintegerfield_config(self, field, key):
        return self.charfield_config(field, key)

    # BOOLEAN
    def booleanfield_config(self, field, key):
        counter = self.get_counter(field, key)
        if counter == 1:
            return True
        elif counter == 2:
            return False
        return random.choice((True, False))

    def nullbooleanfield_config(self, field, key):
        counter = self.get_counter(field, key)
        if counter == 1:
            return None
        elif counter == 2:
            return True
        elif counter == 3:
            return False
        return random.choice((None, True, False))

    # DATE/TIME RELATED
    def datefield_config(self, field, key):
        integer = self.random_integer(field, key, signed=False)
        return date.today() - timedelta(days=integer)

    def timefield_config(self, field, key):
        integer = self.random_integer(field, key, signed=False)
        return (now() - timedelta(seconds=integer)).time()

    def datetimefield_config(self, field, key):
        integer = self.random_integer(field, key, signed=False)
        return now() - timedelta(seconds=integer)

    # FORMATTED STRINGS
    def emailfield_config(self, field, key):
        return f'a{self.random_string(field, key)}@dynamicfixture.com'

    def urlfield_config(self, field, key):
        return f'http://dynamicfixture{self.random_string(field, key)}.com'

    # Deprecated in Django >= 1.7
    def ipaddressfield_config(self, field, key):
        MAX_IP = 2 ** 32 - 1

        integer = self.random_integer(field, key, signed=False)
        integer %= MAX_IP
        return str(socket.inet_ntoa(struct.pack('!L', integer)))

    def xmlfield_config(self, field, key):
        return f'<a>{self.random_string(field, key)}</a>'

    # FILES
    def filepathfield_config(self, field, key):
        return self.random_string(field, key)

    def filefield_config(self, field, key):
        return self.random_string(field, key)

    def imagefield_config(self, field, key):
        return self.random_string(field, key)