# -*- coding: utf-8 -*-
import re
from calendar import TimeEncoding, month_name
from datetime import date
from django import forms
from django.conf import settings
from django.core import exceptions, validators
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models
from django.utils.safestring import mark_safe
from django.utils.text import capfirst
from django.utils.translation import ugettext_lazy as _
from south.modelsinspector import add_introspection_rules
try:
import json
except:
from django.utils import simplejson as json
FILE_TYPES = ('PDF', 'DOC', 'TXT', 'OTHER')
IMG_TYPES = ('JPG', 'BMP', 'PNG', 'GIF',)
PURPOSES = ('photo', 'flat')
# dont delete
class EnumField(models.Field):
"""
A field class that maps to MySQL's ENUM type.
Usage:
Class Card(models.Model):
suit = EnumField(values=('Clubs', 'Diamonds', 'Spades', 'Hearts'))
c = Card()
c.suit = 'Clubs'
c.save()
"""
def __init__(self, *args, **kwargs):
self.values = kwargs.pop('values')
kwargs['choices'] = [(v, v) for v in self.values]
kwargs['default'] = self.values[0]
super(EnumField, self).__init__(*args, **kwargs)
def db_type(self, connection):
return "enum({0})".format( ','.join("'%s'" % v for v in self.values) )
#snippet http://djangosnippets.org/snippets/1478/
class JSONField(models.TextField):
"""JSONField is a generic textfield that neatly serializes/unserializes
JSON objects seamlessly"""
# Used so to_python() is called
__metaclass__ = models.SubfieldBase
def to_python(self, value):
"""Convert our string value to JSON after we load it from the DB"""
if not isinstance(value, basestring):
return value
try:
return json.loads(value)
except ValueError, e:
# If string could not parse as JSON it's means that it's Python
# string saved to JSONField.
return value
def get_db_prep_value(self, value, connection, prepared=True):
"""Convert our JSON object to a string before we save"""
return json.dumps(value)
'''
def get_db_prep_save(self, value, connection):
"""Convert our JSON object to a string before we save"""
if value == "":
return None
if isinstance(value, dict):
value = json.dumps(value, cls=DjangoJSONEncoder)
return value
'''
DEFAULT_WIDTH = 590
DEFAULT_HEIGHT = 200
DEFAULT_LAT = 55.75
DEFAULT_LNG = 37.62
DEFAULT_ADDRESS = u'(Не задано)'
class LocationWidget(forms.TextInput):
def __init__(self, *args, **kw):
self.map_width = kw.get("map_width", DEFAULT_WIDTH)
self.map_height = kw.get("map_height", DEFAULT_HEIGHT)
super(LocationWidget, self).__init__(*args, **kw)
self.inner_widget = forms.widgets.HiddenInput()
def render(self, name, value, *args, **kwargs):
"""
handle on the rendering on the server
"""
if value is None or value==u'':
lat, lng, address = DEFAULT_LAT, DEFAULT_LNG, DEFAULT_ADDRESS
value = {'lat': lat, 'lng': lng, 'address': address}
else:
if isinstance(value, basestring):
a = json.loads(value)
lat, lng, address = float(a['lat']), float(a['lng']), a['address']
else:
lat, lng, address = float(value['lat']), float(value['lng']), value['address']
if isinstance(value, basestring):
curLocation = value
else:
curLocation = json.dumps(value, cls=DjangoJSONEncoder)
js = '''
''' % dict(functionName=name.replace('-', '_'), name=name, lat=lat, lng=lng, defAddress=DEFAULT_ADDRESS)
html = self.inner_widget.render("%s" % name, "%s" % curLocation, dict(id='id_%s' % name))
html += ' ' % ( name)
html += '
' % (name, self.map_width, self.map_height)
html += ' %s' % (u'Текущий адрес', address)
return mark_safe(js + html)
class Media:
css = {'all': (
'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/redmond/jquery-ui.css',
settings.MEDIA_URL+'css/main.css',
)}
js = (
'http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js',
'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js',
'http://maps.google.com/maps/api/js?sensor=false',
)
class LocationField(JSONField):
def formfield(self, **kwargs):
defaults = {'widget': LocationWidget}
return super(LocationField, self).formfield(**defaults)
_regex_validator = validators.RegexValidator(
regex=re.compile('^\[(u(\'|\")([1-9]|1[0-2])/[0-9]{4}(\'|\"),?\s?)+\]$'))
class MonthMultiSelectField(models.CharField):
__metaclass__ = models.SubfieldBase
_saved_month_names = None
_saved_choices_month = None
_saved_choices = None
_saved_choices_values = None
_inst_saved_choices = set()
_inst_saved_choices_values = set()
def __init__(self, *args, **kwargs):
# FIXME: need to reset if choices are given
# _saved_choices
# _saved_choices_values
# _saved_choices_month
choices = kwargs.pop('choices', None)
self.value_type = kwargs.pop('value_type', None)
super(MonthMultiSelectField, self).__init__(*args, **kwargs)
self.validators.append(_regex_validator)
if choices is None:
self._choices = self.make_choices()
@property
def flatchoices(self):
return None
@classmethod
def get_month_names(cls, month_num, locale=('ru_RU', 'UTF-8')):
'''Usage:
get_months_names(1, ('ru_RU', 'UTF-8'))
'''
with TimeEncoding(locale) as encoding:
if not isinstance(cls._saved_month_names, list):
cls._saved_month_names = list(month_name)
m = cls._saved_month_names[month_num].decode(encoding or 'utf-8')
return m
@classmethod
def make_choice_display(cls, m, y):
return _(u'%(month)s %(year)s') % {'month': cls.get_month_names(m), 'year': y}
@staticmethod
def get_data_from_string(s):
p = re.compile('^(?P([1-9]|[1][0-2]))/(?P[0-9]{4})$')
m = p.match(s)
return m.group('month'), m.group('year')
@classmethod
def get_month_from_string_display(cls, s):
return cls.make_choice_display(*self.get_month_from_string(s))
@classmethod
def _make_choice_map_func(cls, *args):
c = ('{m}/{y}'.format(m=args[0], y=args[1]), cls.make_choice_display(*args))
cls._saved_choices_values.add(c[0])
return c
@classmethod
def _make_choices(cls):
today = date.today()
#years = [today.year, today.year + 1] # map(lambda x: today.year + x, xrange(0, 2))
cls._saved_choices_month == today.replace(day=1)
choices = []
cls._saved_choices_values = set()
for year in [today.year, today.year + 1]:
year_choices = map(
lambda m: cls._make_choice_map_func(m, year),
xrange(today.month if year == today.year else 1, 13)
)
choices.append((year, year_choices))
return choices
@classmethod
def _check_relevance(cls):
return cls._saved_choices_month == date.today().replace(day=1)
def make_choices(self):
if self._saved_choices is not None and self._check_relevance():
return self._saved_choices
return self._make_choices()
@property
def flatchoices(self):
return None
def get_choices_default(self):
return self.get_choices(include_blank=False)
def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
return self.get_prep_value(value)
def validate(self, value, model_instance):
if not self.editable:
# Skip validation for non-editable fields.
return
for opt_select in value:
if not self.valid_value(opt_select):
if django.VERSION[0] >= 1 and django.VERSION[1] >= 6:
raise exceptions.ValidationError(self.error_messages['invalid_choice'] % {"value": value})
else:
raise exceptions.ValidationError(self.error_messages['invalid_choice'] % value)
def valid_value(self, value):
"Check to see if the provided value is a valid choice"
if value in self._saved_choices_values:
return True
return False
def formfield(self, **kwargs):
"""
Returns a django.forms.Field instance for this database Field.
"""
defaults = {
'choices': self.get_choices_default(),
'required': not self.blank,
'label': capfirst(self.verbose_name),
'help_text': self.help_text,
'initial': self.get_default(),
'validators': [_regex_validator],
}
defaults.update(kwargs)
return forms.MultipleChoiceField(**defaults)
def get_prep_value(self, value):
if value in validators.EMPTY_VALUES:
return self.get_default()
return ",".join(value)
def to_python(self, value):
if any([value is None, not value]):
return []
else:
if isinstance(value, set):
value = sorted(list(value))
value_list = value if isinstance(value, list) else value.split(',')
if self.value_type is not None:
value_list = map(self.value_type, value_list)
return value_list
def contribute_to_class(self, cls, name):
super(MonthMultiSelectField, self).contribute_to_class(cls, name)
if self.choices:
def get_list(obj):
fieldname = name
choicedict = dict(self.choices)
display = []
values = getattr(obj, fieldname)
if values:
display = map(
lambda x: self.get_month_from_string_display(x),
values)
return display
def get_display(obj):
return ", ".join(get_list(obj))
setattr(cls, 'get_%s_list' % self.name, get_list)
setattr(cls, 'get_%s_display' % self.name, get_display)
add_introspection_rules([
(
[EnumField], # Class(es) these apply to
[], # Positional arguments (not used)
{ # Keyword argument
"values": ["values", {'default': None}],
},
),
], ["^functions\.custom_fields\.EnumField", "^functions\.custom_fields\.LocationField"])
add_introspection_rules([], ["^functions\.custom_fields\.MonthMultiSelectField"])