Skip to content

Instantly share code, notes, and snippets.

@matthiask
Created December 21, 2016 15:12
Show Gist options
  • Save matthiask/318e1f0a837b28a9e204319ec0dc6e11 to your computer and use it in GitHub Desktop.
Save matthiask/318e1f0a837b28a9e204319ec0dc6e11 to your computer and use it in GitHub Desktop.
Postal addresses
# coding=utf-8
from __future__ import print_function
from bs4 import BeautifulSoup
import re
import requests
from django.core.management.base import BaseCommand
from django.utils.encoding import force_text
from django_countries.data import COUNTRIES
from bonheur.contacts.models import PostalAddressTemplate
COUNTRY_REVERSE_MAP = dict(
(value, [key]) for key, value in COUNTRIES.items())
COUNTRY_REVERSE_MAP.setdefault(
u'Croatia/Serbia/ Slovenia (former\n Yugoslavia)',
[],
).extend(['HR', 'RS', 'SI', 'MK'])
COUNTRY_REVERSE_MAP.setdefault(u'Korea', []).extend(['KP', 'KR'])
COUNTRY_REVERSE_MAP.setdefault(u'Latin America', []).extend([])
COUNTRY_REVERSE_MAP.setdefault(u'Russia', []).extend(['RU'])
class Command(BaseCommand):
def handle(self, **options):
PostalAddressTemplate.objects.all().delete()
response = requests.get(
'http://msdn.microsoft.com/en-us/library/cc195167(d=printer).aspx')
soup = BeautifulSoup(response.content)
topic = soup.find_all('div', attrs={'class': re.compile('topic')})[0]
non_matching_countries = []
first = True
templates = {}
for row in topic.find_all('tr'):
if first:
# Skip header
first = False
continue
try:
country = force_text(row.find_all('b')[0].contents[0])
except IndexError:
continue
template = u'\n'.join(
force_text(comp) for comp in row.find_all('td')[1].contents)
try:
template = template.replace(
'\n', ' ').replace(
'&lt;', '<').replace(
'&gt;', '>').replace(
'<br />', '\n')
lines = template.splitlines()
lines = [line.strip() for line in lines if re.match(
r'^\s*(\[?<[A-Z][A-Za-z0-9\s]+>\]?[\s\(\),]*)+\s*$', line)]
lines = [re.sub(r'\s+', ' ', line) for line in lines]
lines = [line for line in lines if '<i>' not in line]
template = u'\n'.join(line for line in lines if line)
except TypeError:
continue
if country.strip().upper() in COUNTRY_REVERSE_MAP:
countrycodes = COUNTRY_REVERSE_MAP[country.strip().upper()]
else:
if country in COUNTRY_REVERSE_MAP:
countrycodes = COUNTRY_REVERSE_MAP[country]
else:
non_matching_countries.append(country)
if not (countrycodes and template):
continue
for code in countrycodes:
templates.setdefault(code, []).append(template)
# Not matched
print(non_matching_countries)
for code, tpls in templates.items():
template = sorted(tpls, key=len, reverse=True)[0]
PostalAddressTemplate.objects.create(
country=code,
template=template,
)
class PostalAddress(ContactItem):
street = models.TextField(_('street'))
po_box = models.CharField(_('PO box'), max_length=100, blank=True)
city = models.CharField(_('city'), max_length=100)
state = models.CharField(
_('state / province'), max_length=100, blank=True)
postcode = models.CharField(_('postcode'), max_length=100)
country = CountryField(_('country'))
objects = ContactItemManager()
class Meta:
ordering = ('-weight', 'type')
verbose_name = _('postal address')
verbose_name_plural = _('postal addresses')
def __str__(self):
return self.city
def render(self, client):
try:
template = PostalAddressTemplate.objects.get(
country=self.country.code).template
except PostalAddressTemplate.DoesNotExist:
template = '''
<CompanyName>
<FirstName>
<Address1>
<PostalCode> <City>
<State> <Province>
<Country>
'''
ctx = {
#'Honorific': self.contact.title,
#'Title': self.contact.title,
#'Job Title': self.contact.function,
# We work with full name instead of first/last name here
'FirstName': self.contact.full_name,
#'LastName': self.contact.
'CompanyName': self.contact.organization,
'Address1': self.street or self.po_box,
'Address': self.street or self.po_box,
# Not needed because self.street may consist of several lines
#'Address2': '',
'City': self.city,
'State': self.state,
'Province': self.state,
'PostalCode': self.postcode,
'Country': self.country.name,
'CountryCode': self.country.code,
}
# Heuristic
if 'home' in self.type.lower():
del ctx['CompanyName']
if self.country == client.country:
del ctx['Country']
del ctx['CountryCode']
# Remove brackets
address = re.sub(r'\[|\]', '', template)
address = re.sub(
r'<([\w\s]+)>',
lambda matchobj: force_text(ctx.get(matchobj.group(1), '')),
address)
# Remove empty lines, strip remaining
address = u'\n'.join(
line for line in (
line1.strip() for line1 in address.splitlines()
) if line,
)
return address
@property
def maps_url(self):
return u'http://maps.google.com/?%s' % urlencode([(
'q',
(u', '.join(
s.strip() for s in (
self.street,
self.city,
force_text(self.country),
)
)).encode('utf-8')
)])
class PostalAddressTemplate(models.Model):
"""
Thanks http://msdn.microsoft.com/en-us/library/cc195167.aspx
"""
country = CountryField(_('country'), unique=True)
template = models.TextField(_('template'))
class Meta:
ordering = ['country']
verbose_name = _('postal address template')
verbose_name_plural = _('postal address templates')
def __str__(self):
return force_text(self.country.name)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment