You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
403 lines
14 KiB
403 lines
14 KiB
# -*- 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 = '''
|
|
<script type="text/javascript">
|
|
//<![CDATA[
|
|
var map_%(functionName)s;
|
|
|
|
function savePosition_%(functionName)s(point, address)
|
|
{
|
|
var input = document.getElementById("id_%(name)s");
|
|
var location = {'lat': point.lat().toFixed(6), 'lng': point.lng().toFixed(6)};
|
|
location.address = '%(defAddress)s';
|
|
if (address) {
|
|
location.address = address;
|
|
}
|
|
input.value = JSON.stringify(location);
|
|
map_%(functionName)s.panTo(point);
|
|
}
|
|
|
|
function load_%(functionName)s() {
|
|
var point = new google.maps.LatLng(%(lat)f, %(lng)f);
|
|
|
|
var options = {
|
|
zoom: 13,
|
|
center: point,
|
|
mapTypeId: google.maps.MapTypeId.ROADMAP
|
|
};
|
|
|
|
map_%(functionName)s = new google.maps.Map(document.getElementById("map_%(name)s"), options);
|
|
|
|
geocoder = new google.maps.Geocoder();
|
|
|
|
var marker = new google.maps.Marker({
|
|
map: map_%(functionName)s,
|
|
position: point,
|
|
draggable: true
|
|
});
|
|
|
|
google.maps.event.addListener(marker, 'dragend', function(mouseEvent) {
|
|
geocoder.geocode({'latLng': mouseEvent.latLng}, function(results, status) {
|
|
if (status == google.maps.GeocoderStatus.OK && results[0]) {
|
|
$('#address_%(name)s').val(results[0].formatted_address);
|
|
savePosition_%(functionName)s(mouseEvent.latLng, results[0].formatted_address);
|
|
}
|
|
else {
|
|
savePosition_%(functionName)s(mouseEvent.latLng);
|
|
}
|
|
});
|
|
});
|
|
|
|
google.maps.event.addListener(map_%(functionName)s, 'click', function(mouseEvent){
|
|
marker.setPosition(mouseEvent.latLng);
|
|
geocoder.geocode({'latLng': mouseEvent.latLng}, function(results, status) {
|
|
if (status == google.maps.GeocoderStatus.OK && results[0]) {
|
|
$('#address_%(name)s').val(results[0].formatted_address);
|
|
savePosition_%(functionName)s(mouseEvent.latLng, results[0].formatted_address);
|
|
}
|
|
else {
|
|
savePosition_%(functionName)s(mouseEvent.latLng);
|
|
}
|
|
});
|
|
});
|
|
|
|
$('#address_%(name)s').autocomplete({
|
|
source: function(request, response) {
|
|
geocoder.geocode({'address': request.term}, function(results, status) {
|
|
response($.map(results, function(item) {
|
|
return {
|
|
value: item.formatted_address,
|
|
location: item.geometry.location
|
|
}
|
|
}));
|
|
})
|
|
},
|
|
select: function(event, ui) {
|
|
marker.setPosition(ui.item.location);
|
|
savePosition_%(functionName)s(ui.item.location, ui.item.value);
|
|
}
|
|
});
|
|
}
|
|
|
|
$(document).ready(function(){
|
|
load_%(functionName)s();
|
|
});
|
|
|
|
//]]>
|
|
</script>
|
|
''' % 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 += '<input style="width: 580px" id="address_%s" type="text"/><br>' % ( name)
|
|
html += '<br/><div id="map_%s" style="width: %dpx; height: %dpx"></div>' % (name, self.map_width, self.map_height)
|
|
|
|
html += '<br /><label>%s: </label><span>%s</span>' % (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_start_month = None
|
|
_saved_choices_start_year = 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)
|
|
return cls._saved_month_names[month_num].decode(encoding)
|
|
|
|
@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):
|
|
pattern = re.compile('^(?P<month>([1-9]|[1][0-2]))/(?P<year>[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))
|
|
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
|
|
|
|
@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"
|
|
print(value)
|
|
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"])
|
|
|