PageRenderTime 40ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/django/contrib/localflavor/se/forms.py

https://code.google.com/p/mango-py/
Python | 157 lines | 124 code | 8 blank | 25 comment | 1 complexity | 0d4e0bb4a0d91c0b81a77a031644ddae MD5 | raw file
Possible License(s): BSD-3-Clause
  1. # -*- coding: utf-8 -*-
  2. """
  3. Swedish specific Form helpers
  4. """
  5. import re
  6. from django import forms
  7. from django.utils.translation import ugettext_lazy as _
  8. from django.core.validators import EMPTY_VALUES
  9. from django.contrib.localflavor.se.utils import (id_number_checksum,
  10. validate_id_birthday, format_personal_id_number, valid_organisation,
  11. format_organisation_number)
  12. __all__ = ('SECountySelect', 'SEOrganisationNumberField',
  13. 'SEPersonalIdentityNumberField', 'SEPostalCodeField')
  14. SWEDISH_ID_NUMBER = re.compile(r'^(?P<century>\d{2})?(?P<year>\d{2})(?P<month>\d{2})(?P<day>\d{2})(?P<sign>[\-+])?(?P<serial>\d{3})(?P<checksum>\d)$')
  15. SE_POSTAL_CODE = re.compile(r'^[1-9]\d{2} ?\d{2}$')
  16. class SECountySelect(forms.Select):
  17. """
  18. A Select form widget that uses a list of the Swedish counties (l?n) as its
  19. choices.
  20. The cleaned value is the official county code -- see
  21. http://en.wikipedia.org/wiki/Counties_of_Sweden for a list.
  22. """
  23. def __init__(self, attrs=None):
  24. from se_counties import COUNTY_CHOICES
  25. super(SECountySelect, self).__init__(attrs=attrs,
  26. choices=COUNTY_CHOICES)
  27. class SEOrganisationNumberField(forms.CharField):
  28. """
  29. A form field that validates input as a Swedish organisation number
  30. (organisationsnummer).
  31. It accepts the same input as SEPersonalIdentityField (for sole
  32. proprietorships (enskild firma). However, co-ordination numbers are not
  33. accepted.
  34. It also accepts ordinary Swedish organisation numbers with the format
  35. NNNNNNNNNN.
  36. The return value will be YYYYMMDDXXXX for sole proprietors, and NNNNNNNNNN
  37. for other organisations.
  38. """
  39. default_error_messages = {
  40. 'invalid': _('Enter a valid Swedish organisation number.'),
  41. }
  42. def clean(self, value):
  43. value = super(SEOrganisationNumberField, self).clean(value)
  44. if value in EMPTY_VALUES:
  45. return u''
  46. match = SWEDISH_ID_NUMBER.match(value)
  47. if not match:
  48. raise forms.ValidationError(self.error_messages['invalid'])
  49. gd = match.groupdict()
  50. # Compare the calculated value with the checksum
  51. if id_number_checksum(gd) != int(gd['checksum']):
  52. raise forms.ValidationError(self.error_messages['invalid'])
  53. # First: check if this is a real organisation_number
  54. if valid_organisation(gd):
  55. return format_organisation_number(gd)
  56. # Is this a single properitor (enskild firma)?
  57. try:
  58. birth_day = validate_id_birthday(gd, False)
  59. return format_personal_id_number(birth_day, gd)
  60. except ValueError:
  61. raise forms.ValidationError(self.error_messages['invalid'])
  62. class SEPersonalIdentityNumberField(forms.CharField):
  63. """
  64. A form field that validates input as a Swedish personal identity number
  65. (personnummer).
  66. The correct formats are YYYYMMDD-XXXX, YYYYMMDDXXXX, YYMMDD-XXXX,
  67. YYMMDDXXXX and YYMMDD+XXXX.
  68. A + indicates that the person is older than 100 years, which will be taken
  69. into consideration when the date is validated.
  70. The checksum will be calculated and checked. The birth date is checked to
  71. be a valid date.
  72. By default, co-ordination numbers (samordningsnummer) will be accepted. To
  73. only allow real personal identity numbers, pass the keyword argument
  74. coordination_number=False to the constructor.
  75. The cleaned value will always have the format YYYYMMDDXXXX.
  76. """
  77. def __init__(self, coordination_number=True, *args, **kwargs):
  78. self.coordination_number = coordination_number
  79. super(SEPersonalIdentityNumberField, self).__init__(*args, **kwargs)
  80. default_error_messages = {
  81. 'invalid': _('Enter a valid Swedish personal identity number.'),
  82. 'coordination_number': _('Co-ordination numbers are not allowed.'),
  83. }
  84. def clean(self, value):
  85. value = super(SEPersonalIdentityNumberField, self).clean(value)
  86. if value in EMPTY_VALUES:
  87. return u''
  88. match = SWEDISH_ID_NUMBER.match(value)
  89. if match is None:
  90. raise forms.ValidationError(self.error_messages['invalid'])
  91. gd = match.groupdict()
  92. # compare the calculated value with the checksum
  93. if id_number_checksum(gd) != int(gd['checksum']):
  94. raise forms.ValidationError(self.error_messages['invalid'])
  95. # check for valid birthday
  96. try:
  97. birth_day = validate_id_birthday(gd)
  98. except ValueError:
  99. raise forms.ValidationError(self.error_messages['invalid'])
  100. # make sure that co-ordination numbers do not pass if not allowed
  101. if not self.coordination_number and int(gd['day']) > 60:
  102. raise forms.ValidationError(self.error_messages['coordination_number'])
  103. return format_personal_id_number(birth_day, gd)
  104. class SEPostalCodeField(forms.RegexField):
  105. """
  106. A form field that validates input as a Swedish postal code (postnummer).
  107. Valid codes consist of five digits (XXXXX). The number can optionally be
  108. formatted with a space after the third digit (XXX XX).
  109. The cleaned value will never contain the space.
  110. """
  111. default_error_messages = {
  112. 'invalid': _('Enter a Swedish postal code in the format XXXXX.'),
  113. }
  114. def __init__(self, *args, **kwargs):
  115. super(SEPostalCodeField, self).__init__(SE_POSTAL_CODE, *args, **kwargs)
  116. def clean(self, value):
  117. return super(SEPostalCodeField, self).clean(value).replace(' ', '')