Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/faker/providers/barcode/en_US/__init__.py : 22%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import re
3from itertools import product
5from .. import Provider as BarcodeProvider
8class Provider(BarcodeProvider):
9 # Source of GS1 country codes: https://gs1.org/standards/id-keys/company-prefix
10 local_prefixes = (
11 *product((0,), range(10)),
12 *product((1,), range(4)),
13 )
15 upc_e_base_pattern = re.compile(r'^\d{6}$')
16 upc_ae_pattern1 = re.compile(
17 r'^(?P<number_system_digit>[01])' # The first digit must be 0 or 1
18 r'(?=\d{11}$)' # followed by 11 digits of which
19 r'(?P<mfr_code>\d{2})' # the first 2 digits make up the manufacturer code,
20 r'(?:(?P<extra>[012])0{4})' # if immediately followed by 00000, 10000, or 20000,
21 r'(?P<product_code>\d{3})' # a 3-digit product code,
22 r'(?P<check_digit>\d)$', # and finally a check digit.
23 )
24 upc_ae_pattern2 = re.compile(
25 r'^(?P<number_system_digit>[01])' # The first digit must be 0 or 1
26 r'(?=\d{11}$)' # followed by 11 digits of which
27 r'(?P<mfr_code>\d{3,4}?)' # the first 3 or 4 digits make up the manufacturer code,
28 r'(?:0{5})' # if immediately followed by 00000,
29 r'(?P<product_code>\d{1,2})' # a 2-digit or single digit product code,
30 r'(?P<check_digit>\d)$', # and finally a check digit.
31 )
32 upc_ae_pattern3 = re.compile(
33 r'^(?P<number_system_digit>[01])' # The first digit must be 0 or 1
34 r'(?=\d{11}$)' # followed by 11 digits of which
35 r'(?P<mfr_code>\d{5})' # the first 5 digits make up the manufacturer code,
36 r'(?:0{4}(?P<extra>[5-9]))' # if immediately followed by 0000 and a 5, 6, 7, 8, or 9,
37 r'(?P<check_digit>\d)$', # and finally a check digit.
38 )
40 def ean13(self, leading_zero=None, prefixes=()):
41 """Generate an EAN-13 barcode.
43 If ``leading_zero`` is ``True``, the leftmost digit of the barcode will be set
44 to ``0``. If ``False``, the leftmost digit cannot be ``0``. If ``None`` (default),
45 the leftmost digit can be any digit.
47 If ``prefixes`` are specified, the result will begin with one of the sequence in ``prefixes``.
48 This option overrides the option ``leading_zero``
50 .. note::
52 EAN-13 barcode that starts with a zero can be converted to UPC-A
53 by dropping the leading zero. This may cause problems with readers that treat
54 all of these code as UPC-A codes and drop the first digit when reading it.
56 You can set the argument ``prefixes`` ( or ``leading_zero`` for convenience) explicitly
57 to avoid or force the generated barcode to start with a zero.
59 You can also generate actual UPC-A barcode with
60 :meth:`upc_a() <faker.providers.barcode.en_US.Provider.upc_a>`.
62 This method uses :meth:`ean() <faker.providers.barcode.en_US.Provider.ean>` under the
63 hood with the ``length`` argument explicitly set to ``13``.
65 :sample:
66 :sample: leading_zero=False
67 :sample: leading_zero=True
68 :sample: prefixes=('00',)
69 :sample: prefixes=('45', '49')
70 """
71 if not prefixes:
72 if leading_zero is True:
73 prefixes = ((0,),)
74 elif leading_zero is False:
75 prefixes = ((self.random_int(1, 9),),)
77 return super().ean13(prefixes=prefixes)
79 def _convert_upc_a2e(self, upc_a):
80 """
81 Convert a 12-digit UPC-A barcode to its 8-digit UPC-E equivalent.
83 Note that not all UPC-A barcodes can be converted.
84 """
85 if not isinstance(upc_a, str):
86 raise TypeError('`upc_a` is not a string')
87 m1 = self.upc_ae_pattern1.match(upc_a)
88 m2 = self.upc_ae_pattern2.match(upc_a)
89 m3 = self.upc_ae_pattern3.match(upc_a)
90 if not any([m1, m2, m3]):
91 raise ValueError('`upc_a` has an invalid value')
92 upc_e_template = '{number_system_digit}{mfr_code}{product_code}{extra}{check_digit}'
93 if m1:
94 upc_e = upc_e_template.format(**m1.groupdict())
95 elif m2:
96 groupdict = m2.groupdict()
97 groupdict['extra'] = str(len(groupdict.get('mfr_code')))
98 upc_e = upc_e_template.format(**groupdict)
99 else:
100 groupdict = m3.groupdict()
101 groupdict['product_code'] = ''
102 upc_e = upc_e_template.format(**groupdict)
103 return upc_e
105 def _upc_ae(self, base=None, number_system_digit=None):
106 """
107 Create a 12-digit UPC-A barcode that can be converted to UPC-E.
109 The expected value of ``base`` is a 6-digit string. If any other value is
110 provided, this method will use a random 6-digit string instead.
112 The expected value of ``number_system_digit`` is the integer ``0`` or ``1``.
113 If any other value is provided, this method will randomly choose from the two.
115 Please also view notes on `upc_a()` and `upc_e()` for more details.
116 """
117 if isinstance(base, str) and self.upc_e_base_pattern.match(base):
118 base = [int(x) for x in base]
119 else:
120 base = [self.random_int(0, 9) for _ in range(6)]
121 if number_system_digit not in [0, 1]:
122 number_system_digit = self.random_int(0, 1)
124 if base[-1] <= 2:
125 code = base[:2] + base[-1:] + [0] * 4 + base[2:-1]
126 elif base[-1] <= 4:
127 code = base[:base[-1]] + [0] * 5 + base[base[-1]:-1]
128 else:
129 code = base[:5] + [0] * 4 + base[-1:]
131 code.insert(0, number_system_digit)
132 weights = [3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3]
133 weighted_sum = sum(x * y for x, y in zip(code, weights))
134 check_digit = (10 - weighted_sum % 10) % 10
135 code.append(check_digit)
136 return ''.join(str(x) for x in code)
138 def upc_a(self, upc_ae_mode=False, base=None, number_system_digit=None):
139 """Generate a 12-digit UPC-A barcode.
141 The value of ``upc_ae_mode`` controls how barcodes will be generated. If ``False``
142 (default), barcodes are not guaranteed to have a UPC-E equivalent. In this mode,
143 the method uses :meth:`ean13 <faker.providers.barcode.Provider.ean13>` under the hood,
144 and the values of ``base`` and ``number_system_digit`` will be ignored.
146 If ``upc_ae_mode`` is ``True``, the resulting barcodes are guaranteed to have a UPC-E
147 equivalent, and the values of ``base`` and ``number_system_digit`` will be used to
148 control what is generated.
150 Under this mode, ``base`` is expected to have a 6-digit string value. If any other value
151 is supplied, a random 6-digit string will be used instead. As for ``number_system_digit``,
152 the expected value is a ``0`` or a ``1``. If any other value is provided, this method
153 will randomly choose from the two.
155 .. important::
157 When ``upc_ae_mode`` is enabled, you might encounter instances where different values
158 of ``base`` (e.g. ``'120003'`` and ``'120004'``) produce the same UPC-A barcode. This
159 is normal, and the reason lies within the whole conversion process. To learn more about
160 this and what ``base`` and ``number_system_digit`` actually represent, please refer
161 to :meth:`upc_e() <faker.providers.barcode.Provider.upc_e>`.
163 :sample:
164 :sample: upc_ae_mode=True, number_system_digit=0
165 :sample: upc_ae_mode=True, number_system_digit=1
166 :sample: upc_ae_mode=True, base='123456', number_system_digit=0
167 :sample: upc_ae_mode=True, base='120003', number_system_digit=0
168 :sample: upc_ae_mode=True, base='120004', number_system_digit=0
169 """
170 if upc_ae_mode is True:
171 return self._upc_ae(base=base, number_system_digit=number_system_digit)
172 else:
173 ean13 = self.ean13(leading_zero=True)
174 return ean13[1:]
176 def upc_e(self, base=None, number_system_digit=None, safe_mode=True):
177 """Generate an 8-digit UPC-E barcode.
179 UPC-E barcodes can be expressed in 6, 7, or 8-digit formats, but this method uses the
180 8 digit format, since it is trivial to convert to the other two formats. The first digit
181 (starting from the left) is controlled by ``number_system_digit``, and it can only be a
182 ``0`` or a ``1``. The last digit is the check digit that is inherited from the UPC-E barcode's
183 UPC-A equivalent. The middle six digits are collectively referred to as the ``base`` (for a
184 lack of a better term).
186 On that note, this method uses ``base`` and ``number_system_digit`` to first generate a
187 UPC-A barcode for the check digit, and what happens next depends on the value of ``safe_mode``.
188 The argument ``safe_mode`` exists, because there are some UPC-E values that share the same
189 UPC-A equivalent. For example, any UPC-E barcode of the form ``abc0000d``, ``abc0003d``, and
190 ``abc0004d`` share the same UPC-A value ``abc00000000d``, but that UPC-A value will only convert
191 to ``abc0000d`` because of (a) how UPC-E is just a zero-suppressed version of UPC-A and (b) the
192 rules around the conversion.
194 If ``safe_mode`` is ``True`` (default), this method performs another set of conversions to
195 guarantee that the UPC-E barcodes generated can be converted to UPC-A, and that UPC-A
196 barcode can be converted back to the original UPC-E barcode. Using the example above, even
197 if the bases ``120003`` or ``120004`` are used, the resulting UPC-E barcode will always
198 use the base ``120000``.
200 If ``safe_mode`` is ``False``, then the ``number_system_digit``, ``base``, and the computed
201 check digit will just be concatenated together to produce the UPC-E barcode, and attempting
202 to convert the barcode to UPC-A and back again to UPC-E will exhibit the behavior described
203 above.
205 :sample:
206 :sample: base='123456'
207 :sample: base='123456', number_system_digit=0
208 :sample: base='123456', number_system_digit=1
209 :sample: base='120000', number_system_digit=0
210 :sample: base='120003', number_system_digit=0
211 :sample: base='120004', number_system_digit=0
212 :sample: base='120000', number_system_digit=0, safe_mode=False
213 :sample: base='120003', number_system_digit=0, safe_mode=False
214 :sample: base='120004', number_system_digit=0, safe_mode=False
215 """
216 if safe_mode is not False:
217 upc_ae = self._upc_ae(base=base, number_system_digit=number_system_digit)
218 return self._convert_upc_a2e(upc_ae)
219 else:
220 upc_ae = self._upc_ae(base=base, number_system_digit=number_system_digit)
221 return upc_ae[0] + ''.join(str(x) for x in base) + upc_ae[-1]