Advertisement
hellsgate

Models for people and bookings

Aug 7th, 2013
180
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 28.22 KB | None | 0 0
  1. # booking/models.py
  2. from django.db import models
  3. from django.core.exceptions import ValidationError
  4.  
  5. from people.models import Customer, UserProfile
  6. from venue.models import Venue, Area, Table, Booth, Package, Timeslot
  7. from corecrm.common import random_ref_id
  8.  
  9. from .choices import DURATION_CHOICES
  10.  
  11. class CustomerPackage(models.Model):
  12.     reference = models.CharField(max_length=50, blank=True)
  13.     package_date = models.DateField(auto_now_add=True)
  14.    
  15.     class Meta:
  16.         ordering = ("-package_date",)
  17.    
  18.    
  19.    
  20.     def __unicode(self):
  21.         return self.reference
  22.  
  23.     def save(self):
  24.         super(CustomerPackage, self).save()
  25.         if self.reference == '':
  26.             self.reference = random_ref_id(self.id)
  27.             self.save()
  28.  
  29.     def _get_customer(self):
  30.         customer=None
  31.         fbs=self.functionbooking_set.all()
  32.         rbs=self.restaurantbooking_set.all()
  33.         nbs=self.nightclubbooking_set.all()
  34.  
  35.         if any(fbs):
  36.             customer=fbs[0].customer
  37.         elif any(rbs):
  38.             customer=rbs[0].customer
  39.         elif any(nbs):
  40.             customer=nbs[0].customer
  41.  
  42.         return customer
  43.     customer = property(_get_customer)
  44.  
  45.     def _get_bookings(self):
  46.         # Amalgamate the bookings of different types into one booking list
  47.         bookings = []
  48.         for fb in self.functionbooking_set.all():
  49.             bookings.append(fb)
  50.         for rb in self.restaurantbooking_set.all():
  51.             bookings.append(rb)
  52.         for nb in self.nightclubbooking_set.all():
  53.             bookings.append(nb)
  54.  
  55.         if any(bookings):
  56.             bookings = sorted(bookings, key=lambda bk: bk.booking_start)
  57.         return bookings
  58.     bookings = property(_get_bookings)
  59.  
  60.     def get_bookings_by_date_range(self, start_date, end_date):
  61.         # Amalgamate the bookings of different types into one booking list with date filter
  62.         bookings = []
  63.         for fb in self.functionbooking_set.filter():
  64.             bookings.append(fb)
  65.         for rb in self.restaurantbooking_set.all():
  66.             bookings.append(rb)
  67.         for nb in self.nightclubbooking_set.all():
  68.             bookings.append(nb)
  69.  
  70.         if any(bookings):
  71.             bookings = sorted(bookings, key=lambda bk: bk.booking_start)
  72.         return bookings
  73.  
  74.     def _get_bookings_for_api(self):
  75.         bookings = []
  76.         for fb in self.functionbooking_set.all():
  77.             bk = {
  78.                 'id': fb.id,
  79.                 'type': fb.booking_type,
  80.                 'customer': fb.customer.__unicode__(),
  81.                 'venue': fb.venue.name,
  82.                 'booking_start': fb.booking_start,
  83.                 'pax': fb.pax,
  84.                 'notes': fb.notes,
  85.                 'area': fb.area.name
  86.             }
  87.             bookings.append(bk)
  88.         for rb in self.restaurantbooking_set.all():
  89.             bk = {
  90.                 'id': rb.id,
  91.                 'type': rb.booking_type,
  92.                 'customer': rb.customer.__unicode__(),
  93.                 'venue': rb.venue.name,
  94.                 'booking_start': rb.booking_start,
  95.                 'pax': rb.pax,
  96.                 'notes': rb.notes,
  97.                 'table': rb.table.number
  98.             }
  99.             bookings.append(bk)
  100.         for nb in self.nightclubbooking_set.all():
  101.             bk = {
  102.                 'id': nb.id,
  103.                 'type': nb.booking_type,
  104.                 'customer': nb.customer.__unicode__(),
  105.                 'venue': nb.venue.name,
  106.                 'booking_start': nb.booking_start,
  107.                 'pax': nb.pax,
  108.                 'notes': nb.notes,
  109.                 'booth': nb.booth.name
  110.             }
  111.             bookings.append(bk)
  112.  
  113.         if any(bookings):
  114.             bookings = sorted(bookings, key=lambda bk: bk['booking_start'])
  115.         return bookings
  116.     api_bookings = property(_get_bookings_for_api)
  117.  
  118.  
  119. class AbstractBaseBooking(models.Model):
  120.     customer = models.ForeignKey(Customer)
  121.     booking_agent = models.ForeignKey(UserProfile)
  122.     venue = models.ForeignKey(Venue)
  123.     duration = models.CharField(max_length=10, choices=DURATION_CHOICES, default='1')
  124.     booked_on = models.DateField(auto_now_add=True)
  125.     booking_start = models.DateTimeField()
  126.     pax = models.IntegerField(verbose_name='Number Of People')
  127.     notes = models.TextField(blank=True, default='')
  128.     customer_package = models.ForeignKey(CustomerPackage)
  129.     guests = models.ManyToManyField(Customer, related_name='%(class)s_guests')
  130.  
  131.     def _calculate_total_duration(self):
  132.         return int(float(self.duration) * 4)
  133.     total_duration = property(_calculate_total_duration)
  134.  
  135.     def __unicode__(self):
  136.         return "%s, %s, %s" % (self.customer.full_name, self.booking_start,
  137.             self.venue.name)
  138.  
  139.     class Meta:
  140.         abstract = True
  141.         ordering = ('booked_on',)
  142.  
  143.  
  144. class FunctionType(models.Model):
  145.     function_type = models.CharField(max_length=32)
  146.  
  147.     def __unicode__(self):
  148.         return self.function_type
  149.  
  150.  
  151. class FunctionBooking(AbstractBaseBooking):
  152.     area = models.ForeignKey(Area, help_text='You can select multiple values by holding down either ctrl (pc) or command (mac)')
  153.     function_type = models.ForeignKey(FunctionType)
  154.  
  155.     booking_type = 'function'
  156.  
  157.     def _get_area_description(self):
  158.         return "%s - %s" % (self.area.name, self.area.description)
  159.     area_detail = property(_get_area_description)
  160.  
  161.  
  162. class RestaurantBooking(AbstractBaseBooking):
  163.     table = models.ForeignKey(Table)
  164.  
  165.     booking_type = 'restaurant'
  166.  
  167.     def _get_area_description(self):
  168.         return "%s - %s" % (self.table.area_layout.area.name,
  169.             self.table.area_layout.area.description)
  170.     area_detail = property(_get_area_description)
  171.  
  172.  
  173. class NightclubBooking(AbstractBaseBooking):
  174.     booth = models.ForeignKey(Booth, null=True, blank=True)
  175.     timeslot = models.ForeignKey(Timeslot, null=True, blank=True)
  176.     package = models.ManyToManyField(Package)
  177.  
  178.     booking_type = 'nightclub'
  179.  
  180.     def _get_area_description(self):
  181.         return "%s - %s" % (self.booth.area_layout.area.name,
  182.             self.booth.area_layout.area.description)
  183.     area_detail = property(_get_area_description)
  184.  
  185.  
  186.  
  187. # people/models.py
  188. from django.contrib.gis.db import models
  189. from django.db.models import Q
  190. from django.conf import settings
  191. from django.contrib.auth.models import AbstractUser, Group
  192. from django.contrib.gis.geos import Point
  193. from django.core.exceptions import ValidationError, MultipleObjectsReturned
  194. from django.utils import timezone
  195.  
  196. import csv
  197. from dateutil import parser
  198. import re
  199. try:
  200.     import simplejson as json
  201. except ImportError:
  202.     import json
  203.  
  204. from audit_log.models.fields import LastUserField
  205. from audit_log.models.managers import AuditLog
  206. from unipath import Path
  207. from south.modelsinspector import add_introspection_rules
  208. add_introspection_rules([], ["^audit_log\.models\.fields\.LastUserField"])
  209.  
  210. from tag.models import Tag
  211.  
  212.  
  213. MALE = 'M'
  214. FEMALE = 'F'
  215. GENDER_CHOICES = (
  216.     (MALE, 'Male'),
  217.     (FEMALE, 'Female')
  218. )
  219.  
  220.  
  221. def validate_format_postcode(postcode):
  222.     # Permitted letters depend upon their position in the postcode.
  223.     alpha1 = "[abcdefghijklmnoprstuwyz]";                    # Character 1
  224.     alpha2 = "[abcdefghklmnopqrstuvwxy]";                    # Character 2
  225.     alpha3 = "[abcdefghjkpmnrstuvwxy]";                      # Character 3
  226.     alpha4 = "[abehmnprvwxy]";                               # Character 4
  227.     alpha5 = "[abdefghjlnpqrstuwxyz]";                       # Character 5
  228.     BFPOa5 = "[abdefghjlnpqrst]{1}";                         # BFPO character 5
  229.     BFPOa6 = "[abdefghjlnpqrstuwzyz]{1}";                    # BFPO character 6
  230.  
  231.     pcexp = []
  232.     # Expression for BF1 type postcodes
  233.     pcexp.append('(?P<part1>bf1)(?P<part2>[0-9]{1}%s%s)' % (BFPOa5, BFPOa6))
  234.     # Expression for postcodes: AN NAA, ANN NAA, AAN NAA, and AANN NAA with a space
  235.     pcexp.append('(?P<part1>%s{1}%s?\d{1,2})(?P<part2>\d{1}%s{2})' % (alpha1, alpha2, alpha5))
  236.     # Expression for postcodes: ANA NAA
  237.     pcexp.append('(?P<part2>%s{1}[0-9]{1}%s{1})(?P<part2>[0-9]{1}%s{2})' % (alpha1, alpha3, alpha5))
  238.     # Expression for postcodes: AANA NAA
  239.     pcexp.append('(?P<part1>%s{1}%s{1}[0-9]{1}%s)(?P<part2>[0-9]{1}%s{2})' % (alpha1, alpha2, alpha4, alpha5))
  240.     # Exception for the special postcode GIR 0AA
  241.     pcexp.append('(?P<part1>gir)(?P<part2>0aa)')
  242.     # Standard BFPO numbers
  243.     pcexp.append('(?P<part1>bfpo)(?P<part2>[0-9]{1,4})')
  244.     # c/o BFPO numbers
  245.     pcexp.append('(?P<part1>bfpo)(?P<part2>c\/o[0-9]{1,3})')
  246.     # Overseas Territories
  247.     pcexp.append('(?P<part1>[a-z]{4})(?P<part2>1zz)')
  248.     # Anquilla
  249.     pcexp.append('/^ai-2640$/')
  250.  
  251.     # Load up the string to check, converting into lowercase
  252.     postcode = postcode.lower().replace(' ', '')
  253.     # Assume we are not going to find a valid postcode
  254.     valid = False
  255.  
  256.     # Check the string against the six types of postcodes
  257.     for regexp in pcexp:
  258.         try:
  259.             m = re.search(regexp, postcode)
  260.             if m:
  261.                 valid = True
  262.                 break
  263.         except:
  264.             pass
  265.  
  266.     # Return with the reformatted valid postcode in uppercase if the postcode was
  267.     # valid
  268.     if valid:
  269.         return "%s %s" % (m.group('part1').upper(), m.group('part2').upper())
  270.     else:
  271.         return False
  272.  
  273.  
  274. def get_customer_from_csv_row(row, csv_file):
  275.     new_customer = None
  276.     if 'email' in csv_file.csv_fields.keys():
  277.         check_email = row[csv_file.csv_fields['email']]
  278.         test_first_name = None
  279.         test_last_name = None
  280.         if 'first_name' in csv_file.csv_fields.keys():
  281.             test_first_name = row[csv_file.csv_fields['first_name']]
  282.         if 'last_name' in csv_file.csv_fields.keys():
  283.             test_last_name = row[csv_file.csv_fields['last_name']]
  284.         try:
  285.             new_customer, exists = (Customer.objects.get_or_create(
  286.                 email=check_email))
  287.             # check the first and last names to determine if we
  288.             # should use this model instance or not
  289.             if (test_first_name is not None and
  290.                 test_last_name is not None):
  291.                 if not (new_customer.last_name in [test_last_name, ''] and
  292.                     new_customer.first_name in [test_first_name, '']):
  293.                     # The names passed in don't match those
  294.                     # currently in the database
  295.                     new_customer = Customer()
  296.         except MultipleObjectsReturned:
  297.             check_customers = Customer.objects.filter(email=check_email)
  298.             for check_customer in check_customers:
  299.                 if (test_first_name is not None and
  300.                     test_last_name is not None):
  301.                     if (check_customer.last_name == test_last_name and
  302.                         check_customer.first_name == test_first_name):
  303.                         # The names passed in don't match those
  304.                         # currently in the database
  305.                         new_customer = check_customer
  306.                         break
  307.             if new_customer is None:
  308.                 new_customer = Customer()
  309.     else:
  310.         new_customer = Customer()
  311.  
  312.     new_customer.is_csv = True
  313.     return new_customer
  314.  
  315.  
  316. class UserProfile(AbstractUser):
  317.     address1 = models.CharField(max_length=255, default='')
  318.     address2 = models.CharField(max_length=255, blank=True)
  319.     middle_name = models.CharField(max_length=255, blank=True,
  320.         verbose_name="Middle Name(s)")
  321.     city = models.CharField(max_length=255, default='')
  322.     region = models.CharField(max_length=255, blank=True)
  323.     country = models.CharField(max_length=255, default='')
  324.     postcode = models.CharField(max_length=10, default='')
  325.     dob = models.DateField(null=True, blank=True)
  326.     gender = models.CharField(max_length=1, choices=GENDER_CHOICES, default='')
  327.     telephone = models.CharField(max_length=20, default='')
  328.     mobile = models.CharField(max_length=20, blank=True)
  329.  
  330.     def __unicode__(self):
  331.         return self.full_name
  332.  
  333.     def save(self, *args, **kwargs):
  334.         if self.username == '':
  335.             self.username = self.email
  336.  
  337.         super(UserProfile, self).save(*args, **kwargs)
  338.  
  339.     def _get_full_name(self):
  340.         if self.middle_name != '':
  341.             return "%s %s %s" % (self.first_name, self.middle_name,
  342.                 self.last_name)
  343.         else:
  344.             return "%s %s" % (self.first_name, self.last_name)
  345.     full_name = property(_get_full_name)
  346.  
  347.     def _get_model_fields(self):
  348.         return self._meta.fields
  349.     model_fields = property(_get_model_fields)
  350. UserProfile._meta.get_field_by_name('username')[0]._max_length = 75
  351.  
  352.  
  353. class CompanyType(models.Model):
  354.     name = models.CharField(max_length=255)
  355.  
  356.     def __unicode__(self):
  357.         return self.name
  358.  
  359.  
  360. class Customer(models.Model):
  361.     first_name = models.CharField(max_length=255, blank=True, db_index=True)
  362.     middle_name = models.CharField(max_length=255, blank=True,
  363.         verbose_name="Middle Name(s)")
  364.     last_name = models.CharField(max_length=255, blank=True, db_index=True)
  365.     email = models.CharField(max_length=255, blank=True, db_index=True)
  366.     date_joined = models.DateTimeField(null=True, blank=True)
  367.     address1 = models.CharField(max_length=255, blank=True)
  368.     address2 = models.CharField(max_length=255, blank=True)
  369.     address3 = models.CharField(max_length=255, blank=True)
  370.     address4 = models.CharField(max_length=255, blank=True)
  371.     address5 = models.CharField(max_length=255, blank=True)
  372.     city = models.CharField(max_length=255, blank=True, db_index=True)
  373.     region = models.CharField(max_length=255, blank=True)
  374.     postcode = models.CharField(max_length=255, blank=True)
  375.     country = models.CharField(max_length=255, blank=True)
  376.     dob = models.DateField(blank=True, null=True)
  377.     gender = models.CharField(max_length=1, choices=GENDER_CHOICES, blank=True)
  378.     telephone = models.CharField(max_length=20, blank=True)
  379.     mobile = models.CharField(max_length=20, blank=True)
  380.     is_company = models.BooleanField(default=False)
  381.     company_type = models.ForeignKey(CompanyType, blank=True, null=True)
  382.     latitude = models.DecimalField(max_digits=12, decimal_places=9, null=True,
  383.         blank=True)
  384.     longitude = models.DecimalField(max_digits=12, decimal_places=9, null=True,
  385.         blank=True)
  386.  
  387.     audit_log = AuditLog()
  388.  
  389.     is_csv = False
  390.  
  391.     def __init__(self, *args, **kwargs):
  392.         super(Customer, self).__init__(*args, **kwargs)
  393.         self.tags = None
  394.         if self.id > 0:
  395.             self.tags = CustomerToTag.objects.filter(customer=self)
  396.  
  397.     def __unicode__(self):
  398.         id_parts = []
  399.         if self.first_name is not '':
  400.             id_parts.append(self.first_name)
  401.         if self.middle_name is not '':
  402.             id_parts.append(self.middle_name)
  403.         if self.last_name is not '':
  404.             id_parts.append(self.last_name)
  405.         if self.email is not '':
  406.             id_parts.append("<%s>" % self.email)
  407.  
  408.         if len(id_parts) > 0:
  409.             return ' '.join(id_parts)
  410.         else:
  411.             return 'No identifying information'
  412.  
  413.     def save(self, *args, **kwargs):
  414.         self.clean()
  415.         if self.postcode != '':
  416.             try:
  417.                 pc = Postcode.objects.get(postcode=self.postcode)
  418.                 self.latitude = pc.latitude
  419.                 self.longitude = pc.longitude
  420.             except Postcode.DoesNotExist:
  421.                 pass
  422.  
  423.         if self.date_joined is not None:
  424.             if timezone.is_naive(self.date_joined):
  425.                 aware_date = timezone.make_aware(self.date_joined, timezone.get_current_timezone())
  426.                 self.date_joined = aware_date
  427.  
  428.         super(Customer, self).save(*args, **kwargs)
  429.  
  430.     def clean(self):
  431.         if self.postcode != '':
  432.             formatted_postcode = validate_format_postcode(self.postcode)
  433.             if not formatted_postcode:
  434.                 if self.is_csv:
  435.                     self.postcode = ''
  436.                 else:
  437.                     raise ValidationError('The postcode is not valid')
  438.             else:
  439.                 self.postcode = formatted_postcode
  440.  
  441.         return super(Customer, self).clean()
  442.  
  443.     def _get_full_name(self):
  444.         name_parts = []
  445.         if self.first_name != '':
  446.             name_parts.append(self.first_name.strip())
  447.         if self.middle_name != '':
  448.             name_parts.append(self.middle_name.strip())
  449.         if self.last_name != '':
  450.             name_parts.append(self.last_name.strip())
  451.  
  452.         if len(name_parts) > 0:
  453.             return ' '.join(name_parts)
  454.         else:
  455.             return 'No name information for this customer'
  456.     full_name = property(_get_full_name)
  457.  
  458.     def _get_model_fields(self):
  459.         return self._meta.fields
  460.     model_fields = property(_get_model_fields)
  461.  
  462.     def add_items_from_csv_row(self, row, csv_file, tag_fields, date_fields):
  463.         for item in row:
  464.             for field_name, column in csv_file.csv_fields.items():
  465.                 '''
  466.                For some reason some of the CSV exports have "\N" as
  467.                a field value. Don't include those fields, or the
  468.                fields which are empty, in the count
  469.                '''
  470.                 if row[column].replace('\N', '').strip() is not '':
  471.                     if field_name not in tag_fields.keys():
  472.                         if field_name in date_fields:
  473.                             if row[column] is not '':
  474.                                 try:
  475.                                     parsed_date = parser.parse(row[column],
  476.                                         dayfirst=True)
  477.                                     # parser creates a naive datetime object so
  478.                                     # we need to make it aware (of timezones)
  479.                                     date_tzaware = timezone.make_aware(
  480.                                         parsed_date,
  481.                                         timezone.get_current_timezone())
  482.                                     setattr(self, field_name, date_tzaware)
  483.                                 except ValueError:
  484.                                     # some of the date fields are
  485.                                     # empty on some rows
  486.                                     pass
  487.                         else:
  488.                             setattr(self, field_name, row[column])
  489.                             if field_name == 'first_name':
  490.                                 # Add the gender if we can work it out
  491.                                 gender = GenderName.objects.filter(
  492.                                     name=row[column]).exclude(gender='')
  493.                                 if len(gender) == 1:
  494.                                     self.gender = gender[0].gender
  495.                     else:
  496.                         '''
  497.                        because of the way Python handles variables (they're not
  498.                        variables! See http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables
  499.                        and
  500.                        http://stackoverflow.com/a/986145/347187)
  501.                        we can update the tag_fields variable here without the
  502.                        need to return it
  503.                        '''
  504.                         tag_fields[field_name] = row[column]
  505.         self.save()
  506.  
  507.     def get_location(self):
  508.         if self.longitude is not None and self.latitude is not None:
  509.             return Point(self.longitude, self.latitude)
  510.  
  511.         return None
  512.  
  513.     class Meta:
  514.         ordering = ['last_name',]
  515.  
  516.  
  517. def filter_for_customers(filters):
  518.     # We can filter by email, first name, last name, telephone or mobile number
  519.     filter_fields = ('email', 'first_name', 'last_name', 'telephone', 'mobile')
  520.     kwargs = {}
  521.     for ff in filter_fields:
  522.         if ff in filters.keys():
  523.             filter_detail = "%s__icontains" % ff
  524.             kwargs[filter_detail] = filters[ff]
  525.  
  526.     if len(kwargs) > 0:
  527.         matching_customers = Customer.objects.filter(**kwargs)
  528.         if matching_customers.count() > 0:
  529.             return matching_customers
  530.  
  531.     return False
  532.  
  533.  
  534. class CustomerToTag(models.Model):
  535.     '''
  536.    We're using a bespoke lookup table here rather than putting a tags
  537.    ManyToManyField in the Customer model as we need to hold some extra info
  538.    about each tag that we can't store using that method
  539.    '''
  540.     SUBSCRIBED = 'S'
  541.     UNSUBSCRIBED = 'U'
  542.     TAG_STATUS_CHOICES = (
  543.         (SUBSCRIBED, 'Subscribed'),
  544.         (UNSUBSCRIBED, 'Unsubscribed')
  545.     )
  546.  
  547.     customer = models.ForeignKey(Customer)
  548.     tag = models.ForeignKey(Tag)
  549.     date_created = models.DateField()
  550.     tag_created_by = models.ForeignKey(UserProfile)
  551.     tag_status = models.CharField(max_length=1, choices=TAG_STATUS_CHOICES)
  552.  
  553.     audit_log = AuditLog()
  554.  
  555.     def __unicode__(self):
  556.         return "%s - %s" % (self.tag.category.name, self.tag.name)
  557.  
  558.     class Meta:
  559.         unique_together = (('customer', 'tag'),)
  560.  
  561.  
  562. def get_customers_by_tag_name(tag, include_unsubscribed = False):
  563.     '''
  564.    Function to return a list of customers who have been assigned a specific
  565.    tag, optionally allowing unsubscribed customers to be included
  566.    '''
  567.     if tag != '':
  568.         kwargs = {'customertotag__tag__name': tag}
  569.         if not include_unsubscribed:
  570.             kwargs['customertotag__tag_status'] = 'S'
  571.         customers = Customer.objects.filter(**kwargs)
  572.         if customers.count() > 0:
  573.             return customers
  574.  
  575.     return None
  576.  
  577.  
  578. def get_customers_by_tag(tag, include_unsubscribed = False):
  579.     '''
  580.    Function to return a list of customers who have been assigned a specific
  581.    tag, optionally allowing unsubscribed customers to be included
  582.    '''
  583.     if tag > 0:
  584.         kwargs = {'customertotag__tag': tag}
  585.         if not include_unsubscribed:
  586.             kwargs['customertotag__tag_status'] = 'S'
  587.         customers = Customer.objects.filter(**kwargs)
  588.         if customers.count() > 0:
  589.             return customers
  590.  
  591.     return None
  592.  
  593.  
  594. FIELD_LIST = (
  595.     ('first_name', 'First Name'),
  596.     ('middle_name', 'Middle Name(s)'),
  597.     ('last_name', 'Surname'),
  598.     ('email', 'Email Address'),
  599.     ('telephone', 'Telephone Number'),
  600.     ('mobile', 'Mobile Number'),
  601.     ('address1', 'Address Line 1'),
  602.     ('address2', 'Address Line 2'),
  603.     ('address3', 'Address Line 3'),
  604.     ('address4', 'Address Line 4'),
  605.     ('address5', 'Address Line 5'),
  606.     ('city', 'Town / City'),
  607.     ('region', 'Region'),
  608.     ('postcode', 'Postcode'),
  609.     ('dob', 'Date of Birth'),
  610.     ('date_joined', 'Date Captured'),
  611.     ('further_education', 'Further Education'),
  612.     ('venue', 'Venue'),
  613.     ('referral_source', 'Referral Source'),
  614.     ('event', 'Event'),
  615. )
  616.  
  617.  
  618. TAG_FIELDS = {
  619.     'further_education': 'Further Education',
  620.     'venue': 'Venue',
  621.     'referral_source': 'Referral Source',
  622.     'event': 'Event'
  623. }
  624.  
  625.  
  626. class CustomerCsvFile(models.Model):
  627.     source = models.CharField(max_length=100, blank=True)
  628.     csv_file = models.FileField(upload_to='csvimport/customer/', max_length=255)
  629.     uploaded_by = models.ForeignKey(UserProfile)
  630.     date_uploaded = models.DateField(auto_now_add=True)
  631.     date_imported = models.DateField(blank=True, null=True)
  632.     field_order = models.TextField(blank=True)
  633.     has_headings = models.BooleanField(default=True, verbose_name='First line is headings')
  634.     reimport_filename = models.CharField(max_length=255, blank=True)
  635.     parent = models.ForeignKey('self', blank=True, null=True)
  636.  
  637.     def __unicode__(self):
  638.         return self.filename
  639.  
  640.     def _get_filename(self):
  641.         return Path(self.csv_file.path).name
  642.     filename = property(_get_filename)
  643.  
  644.     def _get_first_line(self):
  645.         if Path(self.csv_file.path).exists():
  646.             with open(self.csv_file.path, 'rb') as f:
  647.                 r = csv.reader(f)
  648.                 if self.has_headings:
  649.                     # skip the first line if it is headings
  650.                     r.next()
  651.                 first_line = r.next()
  652.                 f.close()
  653.                 return first_line
  654.         else:
  655.             return False
  656.     first_line = property(_get_first_line)
  657.  
  658.     def _file_exists(self):
  659.         return Path(self.csv_file.path).exists()
  660.     file_exists = property(_file_exists)
  661.  
  662.     def get_lines(self, num_lines):
  663.         if Path(self.csv_file.path).exists():
  664.             #csv_file = self.csv_file_object
  665.             rows = []
  666.             r = csv.reader(self.csv_file_object)
  667.             if self.has_headings:
  668.                 # skip the first line if it is headings
  669.                 r.next()
  670.             # We're going to work under the assumption that the first empty row
  671.             # indicates the end of the file and the loop should therefore end
  672.             for i in range(num_lines):
  673.                 try:
  674.                     row = r.next()
  675.                 except StopIteration:
  676.                     break
  677.                 if not any(row):
  678.                     break
  679.                 rows.append(row)
  680.             self.csv_file_object.close()
  681.             return rows
  682.  
  683.         return False
  684.  
  685.     def _get_file(self):
  686.         if Path(self.csv_file.path).exists():
  687.             f = open(self.csv_file.path, 'rb')
  688.             return f
  689.  
  690.         return False
  691.     csv_file_object = property(_get_file)
  692.  
  693.     def _get_best_row(self):
  694.         '''
  695.        Grab the row which has the most columns completed to allow for the most
  696.        comprehensive initial data assignation
  697.        '''
  698.         r = csv.reader(self.csv_file_object)
  699.         if self.has_headings:
  700.             # skip the first line if it is headings
  701.             r.next()
  702.  
  703.         current_counter = 0
  704.         biggest_row = []
  705.         for row in r:
  706.             counter = 0
  707.             # Check that the line isn't empty
  708.             if any(row):
  709.                 '''
  710.                For some reason some of the CSV exports have "\N" as a field
  711.                value. Don't count those fields, or the fields which are empty,
  712.                in the count
  713.                '''
  714.                 for value in row:
  715.                     if value.replace('\N', '').strip() != '':
  716.                         counter += 1
  717.  
  718.                 if (counter > current_counter):
  719.                     current_counter = counter
  720.                     biggest_row = row
  721.  
  722.         return biggest_row
  723.     best_row = property(_get_best_row)
  724.  
  725.     def _get_row_count(self):
  726.         r = csv.reader(self.csv_file_object)
  727.         if self.has_headings:
  728.             # skip the first line if it is headings
  729.             r.next()
  730.         counter = 0
  731.         for row in r:
  732.             if any(row):
  733.                 counter += 1
  734.  
  735.         self.csv_file_object.seek(0)
  736.         return counter
  737.     row_count = property(_get_row_count)
  738.  
  739.     def _get_csv_fields(self):
  740.         if self.field_order != '':
  741.             field_list = json.loads(self.field_order)
  742.             field_dict = {}
  743.             for counter, item in enumerate(field_list):
  744.                 if (item['field_name'] != 'unknown' and
  745.                     item['field_name'].replace('\N', '').strip() is not ''):
  746.                     field_dict[item['field_name']] = counter
  747.  
  748.             return field_dict
  749.  
  750.         return {}
  751.     csv_fields = property(_get_csv_fields)
  752.  
  753.     def _get_parent_field_order(self):
  754.         if self.parent is not None:
  755.             return self.parent.field_order
  756.         else:
  757.             return False
  758.     parent_field_order = property(_get_parent_field_order)
  759.  
  760.  
  761. class GenderName(models.Model):
  762.     name = models.CharField(max_length=100, unique=True)
  763.     gender = models.CharField(max_length=1, blank=True, db_index=True)
  764.     latitude = models.DecimalField(max_digits=12, decimal_places=9, null=True,
  765.         blank=True)
  766.     longitude = models.DecimalField(max_digits=12, decimal_places=9, null=True,
  767.         blank=True)
  768.  
  769.     def __unicode__(self):
  770.         gender = (self.gender if self.gender in [MALE, FEMALE] else 'Not Set')
  771.         return "%s (%s)" % (self.name, gender)
  772.  
  773.  
  774. class Postcode(models.Model):
  775.     postcode = models.CharField(max_length=8)
  776.     latitude = models.DecimalField(max_digits=15, decimal_places=13)
  777.     longitude = models.DecimalField(max_digits=15, decimal_places=13)
  778.  
  779.     def __unicode__(self):
  780.         return "%s (%s, %s)" % (self.postcode, self.longitude, self.latitude)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement