Create clusters for all countries - for very low zoom level .

def create_country_clusters():
    """ Create clusters for all countries - for very low zoom level. """
    clusters = []
    main_locations = Country.objects.all()
    for c in main_locations:
        try:
            l = Location.objects.get(country_code=c.code, kind='PPLC')
            cluster = {
                'lat': l.latitude,
                'lng': l.longitude,
                'counter': MapPointer.objects.filter(
                    location__in=c.location.get_children_id_list()).count(),
            }
            clusters.append(cluster)
        except Location.DoesNotExist:
            logger.info(u"Cannot find capital location for %s" % c.code)
    redis_cache.set('allcountries', clusters, timeout=None)
    return clusters

def
create_country_clusters
(
)
:
clusters
=
[
]
main_locations
=
Country
.
objects
.
all
(
)
for
c
in
main_locations
:
try
:
l
=
Location
.
objects
.
get
(
country_code
=
c
.
code
,
kind
=
'PPLC'
)
cluster
=
{
'lat'
:
l
.
latitude
,
'lng'
:
l
.
longitude
,
'counter'
:
MapPointer
.
objects
.
filter
(
location__in
=
c
.
location
.
get_children_id_list
(
)
)
.
count
(
)
,
}
clusters
.
append
(
cluster
)
except
Location
.
DoesNotExist
:
logger
.
info
(
u"Cannot find capital location for %s"
%
c
.
code
)
redis_cache
.
set
(
'allcountries'
,
clusters
,
timeout
=
)
return
clusters


Crate clusters for map when zoom is less then 10 . As above this function takes latitude longitude and map zoom level as argments and returns array of marker positions along with number of items in requested region .

def create_clusters(lat, lng, zoom):
    """
    Crate clusters for map when zoom is less then 10. As above, this function
    takes latitude, longitude and map zoom level as argments and returns array
    of marker positions along with number of items in requested region.
    """
    zoom = int(zoom)
    if zoom >= 6:
        return create_region_clusters(lat, lng, zoom)
    else:
        results = redis_cache.get("allcountries")
        if not results:
            results = create_country_clusters()
            redis_cache.set("allcountries", results, timeout=None)
        return results

def create_clusters ( lat , lng , zoom ) : zoom = int ( zoom ) if zoom >= 6 : return create_region_clusters ( lat , lng , zoom ) else : results = redis_cache . get ( "allcountries" ) if not results : results = create_country_clusters ( ) redis_cache . set ( "allcountries" , results , timeout = None ) return results


Round floating point number to selected precision .

def round_to(n, precision):
    """ Round floating point number to selected precision.
    """
    correction = 0.5 if n >= 0 else -0.5
    return int( n/precision+correction ) * precision

def round_to ( n , precision ) : correction = 0.5 if n >= 0 else - 0.5 return int ( n / precision + correction ) * precision


def round_to_05(n):
    """ Round float to nearest 0.5.
    """
    return round_to(n, 0.5)

def round_to_05 ( n ) : return round_to ( n , 0.5 )


A little helper to decipher GET params into tuples .

def get_boundaries(ne, sw):
    """ A little helper to decipher GET params into tuples.
    """
    return {
        'ne': tuple(float(x) for x in ne.split('x')),
        'sw': tuple(float(x) for x in sw.split('x')), }

def get_boundaries ( ne , sw ) : return { 'ne' : tuple ( float ( x ) for x in ne . split ( 'x' ) ) , 'sw' : tuple ( float ( x ) for x in sw . split ( 'x' ) ) , }


def filter_markers(south_west, north_east):
    """ Filter markers for selected viewport.
    """
    boundaries = get_boundaries(south_west, north_east)
    min_lat, min_lng = boundaries['ne']
    max_lat, max_lng = boundaries['sw']
    diff = max([max_lat - min_lat, max_lng - min_lng, ]) / 4
    qs = MapPointer.objects.filter(latitude__gte=min_lat - diff,
                                   latitude__lte=max_lat + diff,
                                   longitude__gte=min_lng - diff,
                                   longitude__lte=max_lng + diff)
    return qs

def filter_markers ( south_west , north_east ) : boundaries = get_boundaries ( south_west , north_east ) min_lat , min_lng = boundaries [ 'ne' ] max_lat , max_lng = boundaries [ 'sw' ] diff = max ( [ max_lat - min_lat , max_lng - min_lng , ] ) / 4 qs = MapPointer . objects . filter ( latitude__gte = min_lat - diff , latitude__lte = max_lat + diff , longitude__gte = min_lng - diff , longitude__lte = max_lng + diff ) return qs


Find main region and country cities for given lat / lng boudaries .

def find_region_cities(south_west, north_east):
    """ Find main region and country cities for given lat/lng boudaries.
    """
    kinds = ['PPLC', 'PPLA', ]
    boundaries = get_boundaries(south_west, north_east)
    min_lat, min_lng = boundaries['ne']
    max_lat, max_lng = boundaries['sw']
    diff = max([max_lat - min_lat, max_lng - min_lng, ]) / 4
    return Location.objects.filter(
        kind__in=kinds,
        latitude__gte=min_lat - diff,
        latitude__lte=max_lat + diff,
        longitude__gte=min_lng - diff,
        longitude__lte=max_lng + diff)

def find_region_cities ( south_west , north_east ) : kinds = [ 'PPLC' , 'PPLA' , ] boundaries = get_boundaries ( south_west , north_east ) min_lat , min_lng = boundaries [ 'ne' ] max_lat , max_lng = boundaries [ 'sw' ] diff = max ( [ max_lat - min_lat , max_lng - min_lng , ] ) / 4 return Location . objects . filter ( kind__in = kinds , latitude__gte = min_lat - diff , latitude__lte = max_lat + diff , longitude__gte = min_lng - diff , longitude__lte = max_lng + diff )


Creates cluster for given main city ( either capital or region capital ) .

def create_cluster(main_city):
    """ Creates cluster for given main city (either capital or region capital).
    """
    id_list = main_city.parent.get_children_id_list()
    return {
        'id': main_city.pk,
        'lat': main_city.latitude,
        'lng': main_city.longitude,
        'count': len(MapPointer.objects.filter(location__pk__in=id_list)),
    }

def create_cluster ( main_city ) : id_list = main_city . parent . get_children_id_list ( ) return { 'id' : main_city . pk , 'lat' : main_city . latitude , 'lng' : main_city . longitude , 'count' : len ( MapPointer . objects . filter ( location__pk__in = id_list ) ) , }


Updates or creates cache entry for selected city cluster .

def update_region_cluster(main_city):
    """ Updates or creates cache entry for selected city cluster.
    """
    cluster = create_cluster(main_city)
    cache.set("clusters_{}".format(main_city.pk), cluster, timeout=None)
    return cluster

def update_region_cluster ( main_city ) : cluster = create_cluster ( main_city ) cache . set ( "clusters_{}" . format ( main_city . pk ) , cluster , timeout = None ) return cluster


Creates entire set of clusters for all countries in database . Use this carefully as this may take A LONG time .

def create_region_clusters():
    """ Creates entire set of clusters for all countries in database. Use this
        carefully as this may take A LONG time.
    """
    main_cities = Location.objects.filter(kind__in=['PPLA', 'PPLC', ])
    clusters = []
    for city in main_cities:
        clusters.append(update_region_cluster(city))
    return clusters

def create_region_clusters ( ) : main_cities = Location . objects . filter ( kind__in = [ 'PPLA' , 'PPLC' , ] ) clusters = [ ] for city in main_cities : clusters . append ( update_region_cluster ( city ) ) return clusters


Try to serve cached content first and fallback to creating new clusters . This is not recommended - it is VERY heavy database operation .

def get_clusters(south_west, north_east):
    """ Try to serve cached content first, and fallback to creating new clusters.
        This is not recommended - it is VERY heavy database operation.
    """
    main_cities = find_region_cities(south_west, north_east)
    clusters = []
    for city in main_cities:
        cluster = cache.get('clusters_{}'.format(city.pk))
        if cluster is None:
            cluster = update_region_cluster(city)
        clusters.append(cluster)
    return clusters

def get_clusters ( south_west , north_east ) : main_cities = find_region_cities ( south_west , north_east ) clusters = [ ] for city in main_cities : cluster = cache . get ( 'clusters_{}' . format ( city . pk ) ) if cluster is None : cluster = update_region_cluster ( city ) clusters . append ( cluster ) return clusters


Returns a list of map points connected with the object .

def get_map_pointers(obj):
    """ Returns a list of map points connected with the object. """
    return MapPointer.objects.for_model(obj)

def get_map_pointers ( obj ) : return MapPointer . objects . for_model ( obj )


def _get_referer(request):
    """Return the HTTP_REFERER, if existing."""
    if 'HTTP_REFERER' in request.META:
        sr = urlsplit(request.META['HTTP_REFERER'])
        return urlunsplit(('', '', sr.path, sr.query, sr.fragment))

def _get_referer ( request ) : if 'HTTP_REFERER' in request . META : sr = urlsplit ( request . META [ 'HTTP_REFERER' ] ) return urlunsplit ( ( '' , '' , sr . path , sr . query , sr . fragment ) )


Supersede the max value and ajust accordingly the label .

def set_max(self, max):
        """Supersede the max value and ajust accordingly the label."""
        pluralized_labels = getattr(self, 'pluralized_labels', None)
        if pluralized_labels:
            self.label = pluralized_labels[max == 1]
        self.max = max

def set_max ( self , max ) : pluralized_labels = getattr ( self , 'pluralized_labels' , None ) if pluralized_labels : self . label = pluralized_labels [ max == 1 ] self . max = max


Normalize data to an unordered list of distinct non empty whitespace - stripped strings .

def to_python(self, value):
        """Normalize data to an unordered list of distinct, non empty, whitespace-stripped strings."""
        value = super(BasicCommaSeparatedUserField, self).to_python(value)
        if value in EMPTY_VALUES:  # Return an empty list if no useful input was given.
            return []
        return list(set([name.strip() for name in value.split(',') if name and not name.isspace()]))


def validate(self, value):
        """Check the limits."""
        super(BasicCommaSeparatedUserField, self).validate(value)
        if value in EMPTY_VALUES:
            return
        count = len(value)
        if self.max and count > self.max:
            raise ValidationError(self.error_messages['max'].format(limit_value=self.max, show_value=count))
        if self.min and count < self.min:
            raise ValidationError(self.error_messages['min'].format(limit_value=self.min, show_value=count))

def validate ( self , value ) : super ( BasicCommaSeparatedUserField , self ) . validate ( value ) if value in EMPTY_VALUES : return count = len ( value ) if self . max and count > self . max : raise ValidationError ( self . error_messages [ 'max' ] . format ( limit_value = self . max , show_value = count ) ) if self . min and count < self . min : raise ValidationError ( self . error_messages [ 'min' ] . format ( limit_value = self . min , show_value = count ) )


def clean(self, value):
        """Check names are valid and filter them."""
        names = super(BasicCommaSeparatedUserField, self).clean(value)
        if not names:
            return []
        user_model = get_user_model()
        users = list(user_model.objects.filter(is_active=True, **{'{0}__in'.format(user_model.USERNAME_FIELD): names}))
        unknown_names = set(names) ^ set([u.get_username() for u in users])
        errors = []
        if unknown_names:
            errors.append(self.error_messages['unknown'].format(users=', '.join(unknown_names)))
        if self.user_filter:
            filtered_names = []
            for u in users[:]:
                try:
                    reason = self.user_filter(u)
                    if reason is not None:
                        users.remove(u)
                        filtered_names.append(
                            self.error_messages[
                                'filtered_user_with_reason' if reason else 'filtered_user'
                            ].format(username=u.get_username(), reason=reason)
                        )
                except ValidationError as e:
                    users.remove(u)
                    errors.extend(e.messages)
            if filtered_names:
                errors.append(self.error_messages['filtered'].format(users=', '.join(filtered_names)))
        if errors:
            raise ValidationError(errors)
        return users

def
clean
(
self
,
value
)
:
names
=
super
(
BasicCommaSeparatedUserField
,
self
)
.
clean
(
value
)
if
not
names
:
return
[
]
user_model
=
get_user_model
(
)
users
=
list
(
user_model
.
objects
.
filter
(
is_active
=
True
,
*
*
{
'{0}__in'
.
format
(
user_model
.
USERNAME_FIELD
)
:
names
}
)
)
unknown_names
=
set
(
names
)
^
set
(
[
u
.
get_username
(
)
for
u
in
users
]
)
errors
=
[
]
if
unknown_names
:
errors
.
append
(
self
.
error_messages
[
'unknown'
]
.
format
(
users
=
', '
.
join
(
unknown_names
)
)
)
if
self
.
user_filter
:
filtered_names
=
[
]
for
u
in
users
[
:
]
:
try
:
reason
=
self
.
user_filter
(
u
)
if
reason
is
not
:
users
.
remove
(
u
)
filtered_names
.
append
(
self
.
error_messages
[
'filtered_user_with_reason'
if
reason
else
'filtered_user'
]
.
format
(
username
=
u
.
get_username
(
)
,
reason
=
reason
)
)
except
ValidationError
as
e
:
users
.
remove
(
u
)
errors
.
extend
(
e
.
messages
)
if
filtered_names
:
errors
.
append
(
self
.
error_messages
[
'filtered'
]
.
format
(
users
=
', '
.
join
(
filtered_names
)
)
)
if
errors
:
raise
ValidationError
(
errors
)
return
users


Join several querysets by a UNION clause . Returns the SQL string and the list of parameters .

def union(self, querysets):
        """
        Join several querysets by a UNION clause. Returns the SQL string and the list of parameters.
        """
        result_sql, result_params = [], []
        for qs in querysets:
            sql, params = qs.query.sql_with_params()
            result_sql.append(sql)
            result_params.extend(params)
        return ' UNION '.join(result_sql), tuple(result_params)

def
union
(
self
,
querysets
)
:
result_sql
,
result_params
=
[
]
,
[
]
for
qs
in
querysets
:
sql
,
params
=
qs
.
query
.
sql_with_params
(
)
result_sql
.
append
(
sql
)
result_params
.
extend
(
params
)
return
' UNION '
.
join
(
result_sql
)
,
tuple
(
result_params
)


Wrap the text and prepend lines with a prefix .

def format_body(sender, body, indent=_("> "), width=WRAP_WIDTH):
    """
    Wrap the text and prepend lines with a prefix.

    The aim is to get lines with at most `width` chars.
    But does not wrap if the line is already prefixed.

    Prepends each line with a localized prefix, even empty lines.
    Existing line breaks are preserved.
    Used for quoting messages in replies.

    """
    indent = force_text(indent)  # join() doesn't work on lists with lazy translation objects ; nor startswith()
    wrapper = TextWrapper(width=width, initial_indent=indent, subsequent_indent=indent)
    # rem: TextWrapper doesn't add the indent on an empty text
    quote = '\n'.join([line.startswith(indent) and indent+line or wrapper.fill(line) or indent for line in body.splitlines()])
    return ugettext("\n\n{sender} wrote:\n{body}\n").format(sender=sender, body=quote)


Prepend a pattern to the subject unless already there .

def format_subject(subject):
    """
    Prepend a pattern to the subject, unless already there.

    Matching is case-insensitive.

    """
    str = ugettext("Re: {subject}")
    pattern = '^' + str.replace('{subject}', '.*') + '$'
    return subject if re.match(pattern, subject, re.IGNORECASE) else str.format(subject=subject)

def
format_subject
(
subject
)
:
str
=
ugettext
(
"Re: {subject}"
)
pattern
=
'^'
+
str
.
replace
(
'{subject}'
,
'.*'
)
+
'$'
return
subject
if
re
.
match
(
pattern
,
subject
,
re
.
IGNORECASE
)
else
str
.
format
(
subject
=
subject
)


def email(subject_template, message_template, recipient_list, object, action, site):
    """Compose and send an email."""
    ctx_dict = {'site': site, 'object': object, 'action': action}
    message = PostmanNotifyMail()
    for recipient in recipient_list:
        message.send(recipient, ctx_dict)

def email ( subject_template , message_template , recipient_list , object , action , site ) : ctx_dict = { 'site' : site , 'object' : object , 'action' : action } message = PostmanNotifyMail ( ) for recipient in recipient_list : message . send ( recipient , ctx_dict )


def email_visitor(object, action, site):
    """Email a visitor."""
    email('postman/email_visitor_subject.txt', 'postman/email_visitor.txt', [object.email], object, action, site)

def email_visitor ( object , action , site ) : email ( 'postman/email_visitor_subject.txt' , 'postman/email_visitor.txt' , [ object . email ] , object , action , site )


def notify_user(object, action, site):
    """Notify a user."""
    if action == 'rejection':
        user = object.sender
        label = 'postman_rejection'
    elif action == 'acceptance':
        user = object.recipient
        parent = object.parent
        label = 'postman_reply' if (parent and parent.sender_id == object.recipient_id) else 'postman_message'
    else:
        return
    if notification:
        # the context key 'message' is already used in django-notification/models.py/send_now() (v0.2.0)
        notification.send(users=[user], label=label, extra_context={'pm_message': object, 'pm_action': action})
    else:
        if not DISABLE_USER_EMAILING and user.email and user.is_active:
            email('postman/email_user_subject.txt', 'postman/email_user.txt', [user.email], object, action, site)


Broadcast a message to multiple Users . For an easier cleanup all these messages are directly marked as archived and deleted on the sender side . The message is expected to be issued from a trusted application so moderation is not necessary and the status is automatically set to accepted .

def pm_broadcast(sender, recipients, subject, body='', skip_notification=False):
    """
    Broadcast a message to multiple Users.
    For an easier cleanup, all these messages are directly marked as archived
    and deleted on the sender side.
    The message is expected to be issued from a trusted application, so moderation
    is not necessary and the status is automatically set to 'accepted'.

    Optional argument:
        ``skip_notification``: if the normal notification event is not wished
    """
    message = Message(subject=subject, body=body, sender=sender,
        sender_archived=True, sender_deleted_at=now(),
        moderation_status=STATUS_ACCEPTED, moderation_date=now())
    if not isinstance(recipients, (tuple, list)):
        recipients = (recipients,)
    for recipient in recipients:
        message.recipient = recipient
        message.pk = None
        message.save()
        if not skip_notification:
            message.notify_users(STATUS_PENDING, _get_site())

def pm_broadcast ( sender , recipients , subject , body = '' , skip_notification = False ) : message = Message ( subject = subject , body = body , sender = sender , sender_archived = True , sender_deleted_at = now ( ) , moderation_status = STATUS_ACCEPTED , moderation_date = now ( ) ) if not isinstance ( recipients , ( tuple , list ) ) : recipients = ( recipients , ) for recipient in recipients : message . recipient = recipient message . pk = None message . save ( ) if not skip_notification : message . notify_users ( STATUS_PENDING , _get_site ( ) )


Write a message to a User . Contrary to pm_broadcast () the message is archived and / or deleted on the sender side only if requested . The message may come from an untrusted application a gateway for example so it may be useful to involve some auto moderators in the processing .

def pm_write(sender, recipient, subject, body='', skip_notification=False,
        auto_archive=False, auto_delete=False, auto_moderators=None):
    """
    Write a message to a User.
    Contrary to pm_broadcast(), the message is archived and/or deleted on
    the sender side only if requested.
    The message may come from an untrusted application, a gateway for example,
    so it may be useful to involve some auto moderators in the processing.

    Optional arguments:
        ``skip_notification``: if the normal notification event is not wished
        ``auto_archive``: to mark the message as archived on the sender side
        ``auto_delete``: to mark the message as deleted on the sender side
        ``auto_moderators``: a list of auto-moderation functions
    """
    message = Message(subject=subject, body=body, sender=sender, recipient=recipient)
    initial_status = message.moderation_status
    if auto_moderators:
        message.auto_moderate(auto_moderators)
    else:
        message.moderation_status = STATUS_ACCEPTED
    message.clean_moderation(initial_status)
    if auto_archive:
        message.sender_archived = True
    if auto_delete:
        message.sender_deleted_at = now()
    message.save()
    if not skip_notification:
        message.notify_users(initial_status, _get_site())

def pm_write ( sender , recipient , subject , body = '' , skip_notification = False , auto_archive = False , auto_delete = False , auto_moderators = None ) : message = Message ( subject = subject , body = body , sender = sender , recipient = recipient ) initial_status = message . moderation_status if auto_moderators : message . auto_moderate ( auto_moderators ) else : message . moderation_status = STATUS_ACCEPTED message . clean_moderation ( initial_status ) if auto_archive : message . sender_archived = True if auto_delete : message . sender_deleted_at = now ( ) message . save ( ) if not skip_notification : message . notify_users ( initial_status , _get_site ( ) )


def clean_recipients(self):
        """Check no filter prohibit the exchange."""
        recipients = self.cleaned_data['recipients']
        exchange_filter = getattr(self, 'exchange_filter', None)
        if exchange_filter:
            errors = []
            filtered_names = []
            recipients_list = recipients[:]
            for u in recipients_list:
                try:
                    reason = exchange_filter(self.instance.sender, u, recipients_list)
                    if reason is not None:
                        recipients.remove(u)
                        filtered_names.append(
                            self.error_messages[
                                'filtered_user_with_reason' if reason else 'filtered_user'
                            ].format(username=u.get_username(), reason=reason)
                        )
                except forms.ValidationError as e:
                    recipients.remove(u)
                    errors.extend(e.messages)
            if filtered_names:
                errors.append(self.error_messages['filtered'].format(users=', '.join(filtered_names)))
            if errors:
                raise forms.ValidationError(errors)
        return recipients

def
clean_recipients
(
self
)
:
recipients
=
self
.
cleaned_data
[
'recipients'
]
exchange_filter
=
getattr
(
self
,
'exchange_filter'
,
)
if
exchange_filter
:
errors
=
[
]
filtered_names
=
[
]
recipients_list
=
recipients
[
:
]
for
u
in
recipients_list
:
try
:
reason
=
exchange_filter
(
self
.
instance
.
sender
,
u
,
recipients_list
)
if
reason
is
not
:
recipients
.
remove
(
u
)
filtered_names
.
append
(
self
.
error_messages
[
'filtered_user_with_reason'
if
reason
else
'filtered_user'
]
.
format
(
username
=
u
.
get_username
(
)
,
reason
=
reason
)
)
except
forms
.
ValidationError
as
e
:
recipients
.
remove
(
u
)
errors
.
extend
(
e
.
messages
)
if
filtered_names
:
errors
.
append
(
self
.
error_messages
[
'filtered'
]
.
format
(
users
=
', '
.
join
(
filtered_names
)
)
)
if
errors
:
raise
forms
.
ValidationError
(
errors
)
return
recipients


Save as many messages as there are recipients .

def save(self, recipient=None, parent=None, auto_moderators=[]):
        """
        Save as many messages as there are recipients.

        Additional actions:
        - If it's a reply, build a conversation
        - Call auto-moderators
        - Notify parties if needed

        Return False if one of the messages is rejected.

        """
        recipients = self.cleaned_data.get('recipients', [])
        if parent and not parent.thread_id:  # at the very first reply, make it a conversation
            parent.thread = parent
            parent.save()
            # but delay the setting of parent.replied_at to the moderation step
        if parent:
            self.instance.parent = parent
            self.instance.thread_id = parent.thread_id
        initial_moderation = self.instance.get_moderation()
        initial_dates = self.instance.get_dates()
        initial_status = self.instance.moderation_status
        if recipient:
            if isinstance(recipient, get_user_model()) and recipient in recipients:
                recipients.remove(recipient)
            recipients.insert(0, recipient)
        is_successful = True
        for r in recipients:
            if isinstance(r, get_user_model()):
                self.instance.recipient = r
            else:
                self.instance.recipient = None
                self.instance.email = r
            self.instance.pk = None  # force_insert=True is not accessible from here
            self.instance.auto_moderate(auto_moderators)
            self.instance.clean_moderation(initial_status)
            self.instance.clean_for_visitor()
            m = super(BaseWriteForm, self).save()
            if self.instance.is_rejected():
                is_successful = False
            self.instance.update_parent(initial_status)
            self.instance.notify_users(initial_status, self.site)
            # some resets for next reuse
            if not isinstance(r, get_user_model()):
                self.instance.email = ''
            self.instance.set_moderation(*initial_moderation)
            self.instance.set_dates(*initial_dates)
        return is_successful


Check that the recipient is correctly initialized .

def clean(self):
        """Check that the recipient is correctly initialized."""
        if not self.recipient:
            raise forms.ValidationError(ugettext("Undefined recipient."))
        return super(BaseReplyForm, self).clean()

def
clean
(
self
)
:
if
not
self
.
recipient
:
raise
forms
.
ValidationError
(
ugettext
(
"Undefined recipient."
)
)
return
super
(
BaseReplyForm
,
self
)
.
clean
(
)


def clean(self):
        """Check data validity and coherence."""
        cleaned_data = super(MessageAdminForm, self).clean()
        sender = cleaned_data.get('sender')
        recipient = cleaned_data.get('recipient')
        email = cleaned_data.get('email')
        errors = []
        if not sender and not recipient:
            errors.append(ugettext("Sender and Recipient cannot be both undefined."))
            if 'sender' in cleaned_data:
                del cleaned_data['sender']
            if 'recipient' in cleaned_data:
                del cleaned_data['recipient']
        elif sender and recipient:
            if email:
                errors.append(ugettext("Visitor's email is in excess."))
                if 'email' in cleaned_data:
                    del cleaned_data['email']
        else:
            if not email:
                errors.append(ugettext("Visitor's email is missing."))
                if 'email' in cleaned_data:
                    del cleaned_data['email']
        sent_at = cleaned_data.get('sent_at')
        read_at = cleaned_data.get('read_at')
        if read_at and read_at < sent_at:
            errors.append(ugettext("Reading date must be later to sending date."))
            if 'read_at' in cleaned_data:
                del cleaned_data['read_at']
        sender_deleted_at = cleaned_data.get('sender_deleted_at')
        if sender_deleted_at and sender_deleted_at < sent_at:
            errors.append(ugettext("Deletion date by sender must be later to sending date."))
            if 'sender_deleted_at' in cleaned_data:
                del cleaned_data['sender_deleted_at']
        recipient_deleted_at = cleaned_data.get('recipient_deleted_at')
        if recipient_deleted_at and recipient_deleted_at < sent_at:
            errors.append(ugettext("Deletion date by recipient must be later to sending date."))
            if 'recipient_deleted_at' in cleaned_data:
                del cleaned_data['recipient_deleted_at']
        replied_at = cleaned_data.get('replied_at')
        obj = self.instance
        if replied_at:
            len_begin = len(errors)
            if replied_at < sent_at:
                errors.append(ugettext("Response date must be later to sending date."))
            if not read_at:
                errors.append(ugettext("The message cannot be replied without having been read."))
            elif replied_at < read_at:
                errors.append(ugettext("Response date must be later to reading date."))
            if not obj.get_replies_count():
                errors.append(ugettext("Response date cannot be set without at least one reply."))
            if not obj.thread_id:
                errors.append(ugettext("The message cannot be replied without being in a conversation."))
            if len(errors) > len_begin:
                if 'replied_at' in cleaned_data:
                    del cleaned_data['replied_at']
        # if obj.parent_id and not obj.thread_id:# can't be set by the form
        if errors:
            raise forms.ValidationError(errors)

        self.initial_status = obj.moderation_status
        return cleaned_data


Add a custom select_related () to avoid a bunch of queries for users in the change list admin view .

def get_queryset(self, request):  # changed in Django 1.6: "The get_queryset method was previously named queryset."
        """
        Add a custom select_related() to avoid a bunch of queries for users
        in the 'change list' admin view.

        Setting 'list_select_related = True' is not efficient as the default
        select_related() does not follow foreign keys that have null=True.

        """
        return super(MessageAdmin, self).get_queryset(request).select_related('sender', 'recipient')


def save_model(self, request, obj, form, change):
        """
        Add some actions around the save.

        Before the save, adjust some constrained fields.
        After the save, update related objects and notify parties if needed.

        """
        obj.clean_moderation(form.initial_status, request.user)
        obj.clean_for_visitor()
        super(MessageAdmin, self).save_model(request, obj, form, change)
        obj.update_parent(form.initial_status)
        obj.notify_users(form.initial_status, get_current_site(request), is_auto_moderated=False)

def save_model ( self , request , obj , form , change ) : obj . clean_moderation ( form . initial_status , request . user ) obj . clean_for_visitor ( ) super ( MessageAdmin , self ) . save_model ( request , obj , form , change ) obj . update_parent ( form . initial_status ) obj . notify_users ( form . initial_status , get_current_site ( request ) , is_auto_moderated = False )


Set status according to the button used to submit .

def clean(self):
        """Set status according to the button used to submit."""
        cleaned_data = super(PendingMessageAdminForm, self).clean()
        obj = self.instance
        self.initial_status = obj.moderation_status
        # look for for button names provided by custom admin/postman/pendingmessage/change_form.html
        if '_saveasaccepted' in self.data:
            obj.set_accepted()
        elif '_saveasrejected' in self.data:
            obj.set_rejected()
        return cleaned_data


def has_add_permission(self, request):
        "Adding is impossible"
        return False

def has_add_permission ( self , request ) : return False


def has_delete_permission(self, request, obj=None):
        "Deleting is impossible"
        return False

def has_delete_permission ( self , request , obj = None ) : return False


Provide the count of unread messages for an authenticated user .

def inbox(request):
    """Provide the count of unread messages for an authenticated user."""
    if request.user.is_authenticated():
        return {'postman_unread_count': Message.objects.inbox_unread_count(request.user)}
    else:
        return {}

def inbox ( request ) : if request . user . is_authenticated ( ) : return { 'postman_unread_count' : Message . objects . inbox_unread_count ( request . user ) } else : return { }


Deferred actions that can not be done at import time since Django 1 . 7 . Normally called in AppConfig . ready () . For backwards compatibility also called on first need .

def setup():
    """
    Deferred actions, that can not be done at import time since Django 1.7.
    Normally called in AppConfig.ready().
    For backwards compatibility, also called on first need.

    """
    try:
        from django.contrib.auth import get_user_model  # Django 1.5
    except ImportError:
        from postman.future_1_5 import get_user_model
    ORDER_BY_FIELDS.update({
        'f': 'sender__' + get_user_model().USERNAME_FIELD,     # as 'from'
        't': 'recipient__' + get_user_model().USERNAME_FIELD,  # as 'to'
        's': 'subject',  # as 'subject'
        'd': 'sent_at',  # as 'date'
    })

def
setup
(
)
:
try
:
from
django
.
contrib
.
auth
import
get_user_model
# Django 1.5
except
ImportError
:
from
postman
.
future_1_5
import
get_user_model
ORDER_BY_FIELDS
.
update
(
{
'f'
:
'sender__'
+
get_user_model
(
)
.
USERNAME_FIELD
,
# as 'from'
't'
:
'recipient__'
+
get_user_model
(
)
.
USERNAME_FIELD
,
# as 'to'
's'
:
'subject'
,
# as 'subject'
'd'
:
'sent_at'
,
# as 'date'
}
)


Return a field name optionally prefixed for descending order or None if not found .

def get_order_by(query_dict):
    """
    Return a field name, optionally prefixed for descending order, or None if not found.

    Argument:
    ``query_dict``: a dictionary to look for a key dedicated to ordering purpose

    """
    if ORDER_BY_KEY in query_dict:
        code = query_dict[ORDER_BY_KEY]  # code may be uppercase or lowercase
        if not ORDER_BY_FIELDS:  # backwards compatibility, before Django 1.7
            setup()
        order_by_field = ORDER_BY_FIELDS.get(code.lower())
        if order_by_field:
            if code.isupper():
                order_by_field = '-' + order_by_field
            return order_by_field


Return a User representation for display configurable through an optional setting .

def get_user_representation(user):
    """
    Return a User representation for display, configurable through an optional setting.
    """
    show_user_as = getattr(settings, 'POSTMAN_SHOW_USER_AS', None)
    if isinstance(show_user_as, six.string_types):
        attr = getattr(user, show_user_as, None)
        if callable(attr):
            attr = attr()
        if attr:
            return force_text(attr)
    elif callable(show_user_as):
        try:
            return force_text(show_user_as(user))
        except:
            pass
    return force_text(user)

def get_user_representation ( user ) : show_user_as = getattr ( settings , 'POSTMAN_SHOW_USER_AS' , None ) if isinstance ( show_user_as , six . string_types ) : attr = getattr ( user , show_user_as , None ) if callable ( attr ) : attr = attr ( ) if attr : return force_text ( attr ) elif callable ( show_user_as ) : try : return force_text ( show_user_as ( user ) ) except : pass return force_text ( user )


def _folder(self, related, filters, option=None, order_by=None):
        """Base code, in common to the folders."""
        qs = self.all() if option == OPTION_MESSAGES else QuerySet(self.model, PostmanQuery(self.model), using=self._db)
        if related:
            qs = qs.select_related(*related)
        if order_by:
            qs = qs.order_by(order_by)
        if isinstance(filters, (list, tuple)):
            lookups = models.Q()
            for filter in filters:
                lookups |= models.Q(**filter)
        else:
            lookups = models.Q(**filters)
        if option == OPTION_MESSAGES:
            return qs.filter(lookups)
            # Adding a 'count' attribute, to be similar to the by-conversation case,
            # should not be necessary. Otherwise add:
            # .extra(select={'count': 'SELECT 1'})
        else:
            qs = qs.extra(select={'count': '{0}.count'.format(qs.query.pm_alias_prefix)})
            qs.query.pm_set_extra(table=(
                # extra columns are always first in the SELECT query
                self.filter(lookups, thread_id__isnull=True).extra(select={'count': 0})\
                    .values_list('id', 'count').order_by(),
                # use separate annotate() to keep control of the necessary order
                self.filter(lookups, thread_id__isnull=False).values('thread').annotate(count=models.Count('pk')).annotate(id=models.Max('pk'))\
                    .values_list('id', 'count').order_by(),
            ))
            return qs


Return accepted messages received by a user but not marked as archived or deleted .

def inbox(self, user, related=True, **kwargs):
        """
        Return accepted messages received by a user but not marked as archived or deleted.
        """
        related = ('sender',) if related else None
        filters = {
            'recipient': user,
            'recipient_archived': False,
            'recipient_deleted_at__isnull': True,
            'moderation_status': STATUS_ACCEPTED,
        }
        return self._folder(related, filters, **kwargs)

def inbox ( self , user , related = True , * * kwargs ) : related = ( 'sender' , ) if related else None filters = { 'recipient' : user , 'recipient_archived' : False , 'recipient_deleted_at__isnull' : True , 'moderation_status' : STATUS_ACCEPTED , } return self . _folder ( related , filters , * * kwargs )


Return the number of unread messages for a user .

def inbox_unread_count(self, user):
        """
        Return the number of unread messages for a user.

        Designed for context_processors.py and templatetags/postman_tags.py

        """
        return self.inbox(user, related=False, option=OPTION_MESSAGES).filter(read_at__isnull=True).count()

def inbox_unread_count ( self , user ) : return self . inbox ( user , related = False , option = OPTION_MESSAGES ) . filter ( read_at__isnull = True ) . count ( )


Return all messages sent by a user but not marked as archived or deleted .

def sent(self, user, **kwargs):
        """
        Return all messages sent by a user but not marked as archived or deleted.
        """
        related = ('recipient',)
        filters = {
            'sender': user,
            'sender_archived': False,
            'sender_deleted_at__isnull': True,
            # allow to see pending and rejected messages as well
        }
        return self._folder(related, filters, **kwargs)


Return messages belonging to a user and marked as archived .

def archives(self, user, **kwargs):
        """
        Return messages belonging to a user and marked as archived.
        """
        related = ('sender', 'recipient')
        filters = ({
            'recipient': user,
            'recipient_archived': True,
            'recipient_deleted_at__isnull': True,
            'moderation_status': STATUS_ACCEPTED,
        }, {
            'sender': user,
            'sender_archived': True,
            'sender_deleted_at__isnull': True,
        })
        return self._folder(related, filters, **kwargs)

def archives ( self , user , * * kwargs ) : related = ( 'sender' , 'recipient' ) filters = ( { 'recipient' : user , 'recipient_archived' : True , 'recipient_deleted_at__isnull' : True , 'moderation_status' : STATUS_ACCEPTED , } , { 'sender' : user , 'sender_archived' : True , 'sender_deleted_at__isnull' : True , } ) return self . _folder ( related , filters , * * kwargs )


Return messages belonging to a user and marked as deleted .

def trash(self, user, **kwargs):
        """
        Return messages belonging to a user and marked as deleted.
        """
        related = ('sender', 'recipient')
        filters = ({
            'recipient': user,
            'recipient_deleted_at__isnull': False,
            'moderation_status': STATUS_ACCEPTED,
        }, {
            'sender': user,
            'sender_deleted_at__isnull': False,
        })
        return self._folder(related, filters, **kwargs)

def trash ( self , user , * * kwargs ) : related = ( 'sender' , 'recipient' ) filters = ( { 'recipient' : user , 'recipient_deleted_at__isnull' : False , 'moderation_status' : STATUS_ACCEPTED , } , { 'sender' : user , 'sender_deleted_at__isnull' : False , } ) return self . _folder ( related , filters , * * kwargs )


Return message / conversation for display .

def thread(self, user, filter):
        """
        Return message/conversation for display.
        """
        return self.select_related('sender', 'recipient').filter(
            filter,
            (models.Q(recipient=user) & models.Q(moderation_status=STATUS_ACCEPTED)) | models.Q(sender=user),
        ).order_by('sent_at')

def thread ( self , user , filter ) : return self . select_related ( 'sender' , 'recipient' ) . filter ( filter , ( models . Q ( recipient = user ) & models . Q ( moderation_status = STATUS_ACCEPTED ) ) | models . Q ( sender = user ) , ) . order_by ( 'sent_at' )


Return messages matching a filter AND being visible to a user as the recipient .

def as_recipient(self, user, filter):
        """
        Return messages matching a filter AND being visible to a user as the recipient.
        """
        return self.filter(filter, recipient=user, moderation_status=STATUS_ACCEPTED)

def as_recipient ( self , user , filter ) : return self . filter ( filter , recipient = user , moderation_status = STATUS_ACCEPTED )


Return messages matching a filter AND being visible to a user as the sender .

def as_sender(self, user, filter):
        """
        Return messages matching a filter AND being visible to a user as the sender.
        """
        return self.filter(filter, sender=user)

def as_sender ( self , user , filter ) : return self . filter ( filter , sender = user )


Return a field - lookups filter as a permission controller for a reply request .

def perms(self, user):
        """
        Return a field-lookups filter as a permission controller for a reply request.

        The user must be the recipient of the accepted, non-deleted, message

        """
        return models.Q(recipient=user) & models.Q(moderation_status=STATUS_ACCEPTED) & models.Q(recipient_deleted_at__isnull=True)

def perms ( self , user ) : return models . Q ( recipient = user ) & models . Q ( moderation_status = STATUS_ACCEPTED ) & models . Q ( recipient_deleted_at__isnull = True )


def set_read(self, user, filter):
        """
        Set messages as read.
        """
        return self.filter(
            filter,
            recipient=user,
            moderation_status=STATUS_ACCEPTED,
            read_at__isnull=True,
        ).update(read_at=now())

def set_read ( self , user , filter ) : return self . filter ( filter , recipient = user , moderation_status = STATUS_ACCEPTED , read_at__isnull = True , ) . update ( read_at = now ( ) )


def get_queryset(self):  # changed in Django 1.6: "The get_queryset method was previously named get_query_set."
        """Filter to get only pending objects."""
        return super(PendingMessageManager, self).get_queryset().filter(moderation_status=STATUS_PENDING)


def set_accepted(self):
        """Set the message as accepted."""
        self.moderation_status = STATUS_ACCEPTED

def set_accepted ( self ) : self . moderation_status = STATUS_ACCEPTED


def set_rejected(self):
        """Set the message as rejected."""
        self.moderation_status = STATUS_REJECTED

def set_rejected ( self ) : self . moderation_status = STATUS_REJECTED


def sub(value, arg):
    """Subtract the arg from the value."""
    try:
        return int(value) - int(arg)
    except (ValueError, TypeError):
        return value

def sub ( value , arg ) : try : return int ( value ) - int ( arg ) except ( ValueError , TypeError ) : return value


Replace the value by a fixed pattern if it equals the argument .

def or_me(value, arg):
    """
    Replace the value by a fixed pattern, if it equals the argument.

    Typical usage: message.obfuscated_sender|or_me:user

    """
    user_model = get_user_model()
    if not isinstance(value, six.string_types):
        value = (get_user_representation if isinstance(value, user_model) else force_text)(value)
    if not isinstance(arg, six.string_types):
        arg = (get_user_representation if isinstance(arg, user_model) else force_text)(arg)
    return _('<me>') if value == arg else value

def or_me ( value , arg ) : user_model = get_user_model ( ) if not isinstance ( value , six . string_types ) : value = ( get_user_representation if isinstance ( value , user_model ) else force_text ) ( value ) if not isinstance ( arg , six . string_types ) : arg = ( get_user_representation if isinstance ( arg , user_model ) else force_text ) ( arg ) return _ ( '<me>' ) if value == arg else value


def compact_date(value, arg):
    """
    Output a date as short as possible.

    The argument must provide 3 patterns: for same day, for same year, otherwise
    Typical usage: |compact_date:_("G:i,j b,j/n/y")

    """
    bits = arg.split(',')
    if len(bits) < 3:
        return value  # Invalid arg.
    today = datetime.date.today()
    return date(value, bits[0] if value.date() == today else bits[1] if value.year == today.year else bits[2])

def
compact_date
(
value
,
arg
)
:
bits
=
arg
.
split
(
','
)
if
len
(
bits
)
<
3
:
return
value
# Invalid arg.
today
=
datetime
.
date
.
today
(
)
return
date
(
value
,
bits
[
0
]
if
value
.
date
(
)
==
today
else
bits
[
1
]
if
value
.
year
==
today
.
year
else
bits
[
2
]
)


Compose a query string to ask for a specific ordering in messages list .

def postman_order_by(parser, token):
    """
    Compose a query string to ask for a specific ordering in messages list.

    The unique argument must be one of the keywords of a set defined in the model.
    Example::

        ...
    """
    try:
        tag_name, field_name = token.split_contents()
        field_code = ORDER_BY_MAPPER[field_name.lower()]
    except ValueError:
        raise TemplateSyntaxError("'{0}' tag requires a single argument".format(token.contents.split()[0]))
    except KeyError:
        raise TemplateSyntaxError(
            "'{0}' is not a valid argument to '{1}' tag."
            " Must be one of: {2}".format(field_name, tag_name, ORDER_BY_MAPPER.keys()))
    return OrderByNode(field_code)


Give the number of unread messages for a user or nothing ( an empty string ) for an anonymous user .

def postman_unread(parser, token):
    """
    Give the number of unread messages for a user,
    or nothing (an empty string) for an anonymous user.

    Storing the count in a variable for further processing is advised, such as::

        {% postman_unread as unread_count %}
        ...
        {% if unread_count %}
            You have <strong>{{ unread_count }}</strong> unread messages.
        {% endif %}
    """
    bits = token.split_contents()
    if len(bits) > 1:
        if len(bits) != 3:
            raise TemplateSyntaxError("'{0}' tag takes no argument or exactly two arguments".format(bits[0]))
        if bits[1] != 'as':
            raise TemplateSyntaxError("First argument to '{0}' tag must be 'as'".format(bits[0]))
        return InboxCountNode(bits[2])
    else:
        return InboxCountNode()


Return a formatted GET query string as ?order_key = order_val .

def render(self, context):
        """
        Return a formatted GET query string, as "?order_key=order_val".

        Preserves existing GET's keys, if any, such as a page number.
        For that, the view has to provide request.GET in a 'gets' entry of the context.

        """
        if 'gets' in context:
            gets = context['gets'].copy()
        else:
            gets = QueryDict('').copy()
        if ORDER_BY_KEY in gets:
            code = gets.pop(ORDER_BY_KEY)[0]
        else:
            code = None
        if self.code:
            gets[ORDER_BY_KEY] = self.code if self.code != code else self.code.upper()
        return '?'+gets.urlencode() if gets else ''

def render ( self , context ) : if 'gets' in context : gets = context [ 'gets' ] . copy ( ) else : gets = QueryDict ( '' ) . copy ( ) if ORDER_BY_KEY in gets : code = gets . pop ( ORDER_BY_KEY ) [ 0 ] else : code = None if self . code : gets [ ORDER_BY_KEY ] = self . code if self . code != code else self . code . upper ( ) return '?' + gets . urlencode ( ) if gets else ''


Return the count of unread messages for the user found in context ( may be 0 ) or an empty string .

def render(self, context):
        """
        Return the count of unread messages for the user found in context,
        (may be 0) or an empty string.
        """
        try:
            user = context['user']
            if user.is_anonymous():
                count = ''
            else:
                count = Message.objects.inbox_unread_count(user)
        except (KeyError, AttributeError):
            count = ''
        if self.asvar:
            context[self.asvar] = count
            return ''
        return count

def render ( self , context ) : try : user = context [ 'user' ] if user . is_anonymous ( ) : count = '' else : count = Message . objects . inbox_unread_count ( user ) except ( KeyError , AttributeError ) : count = '' if self . asvar : context [ self . asvar ] = count return '' return count


Delete topic from discussion list via AJAX request .

def delete_topic(request):
    """ Delete topic from discussion list via AJAX request. """
    pk = request.POST.get('object_pk')

    if not pk:
        return HttpResponse(json.dumps({
            'success': False,
            'message': _("No entry ID provided"),
            'level': 'danger',
        }))

    try:
        topic = Discussion.objects.get(pk=pk)
    except Discussion.DoesNotExist as ex:
        return HttpResponse(json.dumps({
            'success': False,
            'message': str(ex),
            'level': 'danger',
        }))

    moderator = is_moderator(request.user, topic.location)
    if request.user != topic.creator and not moderator:
        return HttpResponse(json.dumps({
            'success': False,
            'message': _("Permission required!"),
            'level': 'danger',
        }))

    try:
        with transaction.commit_on_success(): topic.delete()
        return HttpResponse(json.dumps({
            'success': True,
            'message': _("Entry deleted"),
            'level': 'success',
        }))
    except Exception as ex:
        return HttpResponse(json.dumps({
            'success': False,
            'message': str(ex),
            'level': 'danger',
        }))

def
delete_topic
(
request
)
:
pk
=
request
.
POST
.
get
(
'object_pk'
)
if
not
pk
:
return
HttpResponse
(
json
.
dumps
(
{
'success'
:
False
,
'message'
:
_
(
"No entry ID provided"
)
,
'level'
:
'danger'
,
}
)
)
try
:
topic
=
Discussion
.
objects
.
get
(
pk
=
pk
)
except
Discussion
.
DoesNotExist
as
ex
:
return
HttpResponse
(
json
.
dumps
(
{
'success'
:
False
,
'message'
:
str
(
ex
)
,
'level'
:
'danger'
,
}
)
)
moderator
=
is_moderator
(
request
.
user
,
topic
.
location
)
if
request
.
user
!=
topic
.
creator
and
not
moderator
:
return
HttpResponse
(
json
.
dumps
(
{
'success'
:
False
,
'message'
:
_
(
"Permission required!"
)
,
'level'
:
'danger'
,
}
)
)
try
:
with
transaction
.
commit_on_success
(
)
:
topic
.
delete
(
)
return
HttpResponse
(
json
.
dumps
(
{
'success'
:
True
,
'message'
:
_
(
"Entry deleted"
)
,
'level'
:
'success'
,
}
)
)
except
Exception
as
ex
:
return
HttpResponse
(
json
.
dumps
(
{
'success'
:
False
,
'message'
:
str
(
ex
)
,
'level'
:
'danger'
,
}
)
)


def reply(request, slug):
    """ Create forum reply. """
    if request.method == 'POST' and request.POST:
        post = request.POST
        topic = Discussion.objects.get(slug=post['discussion'])
        if not topic.status:
            return HttpResponse(_('This discussion is closed.'))
        entry = Entry(
            content = post['content'],
            creator = request.user,
            discussion = topic,
        )

        entry.save()
        action.send(
            request.user,
            action_object=entry,
            target = topic,
            verb= _('posted')
        )

    return HttpResponseRedirect(request.META['HTTP_REFERER'] + '#reply-' + str(entry.pk))

def
reply
(
request
,
slug
)
:
if
request
.
method
==
'POST'
and
request
.
POST
:
post
=
request
.
POST
topic
=
Discussion
.
objects
.
get
(
slug
=
post
[
'discussion'
]
)
if
not
topic
.
status
:
return
HttpResponse
(
_
(
'This discussion is closed.'
)
)
entry
=
Entry
(
content
=
post
[
'content'
]
,
creator
=
request
.
user
,
discussion
=
topic
,
)
entry
.
save
(
)
action
.
send
(
request
.
user
,
action_object
=
entry
,
target
=
topic
,
verb
=
_
(
'posted'
)
)
return
HttpResponseRedirect
(
request
.
META
[
'HTTP_REFERER'
]
+
'#reply-'
+
str
(
entry
.
pk
)
)


def vote(request, pk):
    """ Vote for reply. """
    entry = Entry.objects.get(pk=pk)
    vote  = False if request.POST.get('vote') == 'false' else True
    user  = request.user
    check = EntryVote.objects.filter(entry=entry).filter(user=user)
    if not len(check):
        entry_vote = EntryVote.objects.create(
            entry = entry,
            user  = user,
            vote  = vote)
        try:
            entry_vote.save()
            context = {
                'success': True,
                'message': _("Vote saved"),
                'votes'  : Entry.objects.get(pk=pk).calculate_votes(),
                'level'  : "success",
            }
        except Exception as ex:
            context = {
                'success': False,
                'message': str(ex),
                'level'  : "danger",
            }
    else:
        context = {
            'success': False,
            'message': _("You already voted on this entry."),
            'level'  : "warning",
        }
    return HttpResponse(json.dumps(context))

def
vote
(
request
,
pk
)
:
entry
=
Entry
.
objects
.
get
(
pk
=
pk
)
vote
=
False
if
request
.
POST
.
get
(
'vote'
)
==
'false'
else
True
user
=
request
.
user
check
=
EntryVote
.
objects
.
filter
(
entry
=
entry
)
.
filter
(
user
=
user
)
if
not
len
(
check
)
:
entry_vote
=
EntryVote
.
objects
.
create
(
entry
=
entry
,
user
=
user
,
vote
=
vote
)
try
:
entry_vote
.
save
(
)
context
=
{
'success'
:
True
,
'message'
:
_
(
"Vote saved"
)
,
'votes'
:
Entry
.
objects
.
get
(
pk
=
pk
)
.
calculate_votes
(
)
,
'level'
:
"success"
,
}
except
Exception
as
ex
:
context
=
{
'success'
:
False
,
'message'
:
str
(
ex
)
,
'level'
:
"danger"
,
}
else
:
context
=
{
'success'
:
False
,
'message'
:
_
(
"You already voted on this entry."
)
,
'level'
:
"warning"
,
}
return
HttpResponse
(
json
.
dumps
(
context
)
)


Notify discussion creator about every new answer .

def response_notification(sender, instance, created, **kwargs):
    """ Notify discussion creator about every new answer. """
    if not created or instance.creator == instance.discussion.creator:
        return True
    notify(instance.creator, instance.discussion.creator,
        key="answer",
        verb=_(u"answered for your discussion"),
        action_object=instance,
        action_target=instance.discussion
    )

def
response_notification
(
sender
,
instance
,
created
,
*
*
kwargs
)
:
if
not
created
or
instance
.
creator
==
instance
.
discussion
.
creator
:
return
True
notify
(
instance
.
creator
,
instance
.
discussion
.
creator
,
key
=
"answer"
,
verb
=
_
(
u"answered for your discussion"
)
,
action_object
=
instance
,
action_target
=
instance
.
discussion
)


Notify entry author about votes for his / her entry .

def vote_notification(sender, instance, created, **kwargs):
    """ Notify entry author about votes for his/her entry. """
    if not created:
        return True
    suff = "up" if instance.vote else "down"
    notify(instance.user, instance.entry.creator,
        key="vote",
        verb=_(u"voted for %s your entry" % suff),
        action_object=instance,
        action_target=instance.entry
    )

def
vote_notification
(
sender
,
instance
,
created
,
*
*
kwargs
)
:
if
not
created
:
return
True
suff
=
"up"
if
instance
.
vote
else
"down"
notify
(
instance
.
user
,
instance
.
entry
.
creator
,
key
=
"vote"
,
verb
=
_
(
u"voted for %s your entry"
%
suff
)
,
action_object
=
instance
,
action_target
=
instance
.
entry
)


A general action that contains tasks . Here we form whole send orders of a given action . A django . contrib . auth . models . User projects . models . Task and verb instance needs to be passed to the function .

def task_action(user, task, verb):
    """
    A general action that contains tasks. Here we form whole send orders
    of a given action. A django.contrib.auth.models.User, projects.models.Task
    and verb instance needs to be passed to the function.
    """
    action.send(user, verb=verb, action_object=task, target=task.group.project)

def task_action ( user , task , verb ) : action . send ( user , verb = verb , action_object = task , target = task . group . project )


def finished_task(user, task):
    """
    The user has finished the task.
    """
    task_action(user, task, _(u"finished task"))
    for participant in task.participants.all():
        notify(user, participant,
            verb=_(u"finished task"),
            key="project",
            action_tartget=task
        )

def
finished_task
(
user
,
task
)
:
task_action
(
user
,
task
,
_
(
u"finished task"
)
)
for
participant
in
task
.
participants
.
all
(
)
:
notify
(
user
,
participant
,
verb
=
_
(
u"finished task"
)
,
key
=
"project"
,
action_tartget
=
task
)


def joined_to_task(user, task):
    """
    The user has joined the task.
    """
    if user != task.creator:
        task_action(user, task, _(u"joined to task"))
        notify(user, task.creator,
            verb=_(u"joined to your task"),
            key="follower",
            action_target=task
        )

def
joined_to_task
(
user
,
task
)
:
if
user
!=
task
.
creator
:
task_action
(
user
,
task
,
_
(
u"joined to task"
)
)
notify
(
user
,
task
.
creator
,
verb
=
_
(
u"joined to your task"
)
,
key
=
"follower"
,
action_target
=
task
)


To this function we pass django user instance and project .

def joined_to_project(user, project):
    """
    To this function we pass django user instance and project.
    """
    if user != project.creator:
        action.send(user, verb=_(u"joined to project"), target=project)
        notify(user, project.creator,
            verb=_(u"joined to your project"),
            key="follower",
            action_target=project
        )

def
joined_to_project
(
user
,
project
)
:
if
user
!=
project
.
creator
:
action
.
send
(
user
,
verb
=
_
(
u"joined to project"
)
,
target
=
project
)
notify
(
user
,
project
.
creator
,
verb
=
_
(
u"joined to your project"
)
,
key
=
"follower"
,
action_target
=
project
)


Same as above but here we inform other poeple that the user has left the project .

def leaved_project(user, project):
    """
    Same as above but here we inform other poeple that the user has left the project.
    """
    action.send(user, verb=_(u"left project"), target=project)

def
leaved_project
(
user
,
project
)
:
action
.
send
(
user
,
verb
=
_
(
u"left project"
)
,
target
=
project
)


The creation of a project should be visible to the followers of the location .

def project_created_action(sender, instance, created, **kwargs):
    """ The creation of a project should be visible to the followers of the location. """
    if created:
        action.send(instance.creator, verb=_(u"created"),
            action_object=instance, target=instance.location)

def project_created_action ( sender , instance , created , * * kwargs ) : if created : action . send ( instance . creator , verb = _ ( u"created" ) , action_object = instance , target = instance . location )


The creation of groups and tasks should be visible only for participants of the given project . Here we also manage topic creation and answers on the forum .

def project_task_action(sender, instance, created, **kwargs):
    """
    The creation of groups and tasks should be visible only for participants of the
    given project. Here we also manage topic creation and answers on the forum.
    """
    if created:
        if hasattr(instance, 'project'):
            # A group of tasks or a discussion was created
            action_target = instance.project
        elif hasattr(instance, 'group'):
            # A task was created
            action_target = instance.group.project
        else:
            # A reply to a discussion was created
            action_target = instance.topic.project
        action.send(instance.creator, verb=_(u"created"),
        action_object=instance, target=action_target)


This function checkes whether the user is allowed to delete or modify the object passed in as an argument . Such an object must be a model instance of this application . i . e . project group of tasks or a task . Returns True / False .

def check_access(obj, user):
    """
    This function checkes whether the user is allowed to delete or modify the
    object passed in as an argument. Such an object must be a model instance
    of this application. i.e. project, group of tasks or a task. Returns
    True/False.
    """
    if user.is_anonymous():
        return False
    # The "creator" can always delete his/her "work"
    access = user == obj.creator
    # The Superadmin is omnipotent
    if not access and user.is_superuser:
        access = True
    # We check the mods rights
    if not access:
        location = None
        if hasattr(obj, 'location'):
            # Project
            location = obj.location
        elif hasattr(obj, 'project'):
            # A Group of taks or a topic in the forum
            location = obj.project.location
        elif hasattr(obj, 'group'):
            # Task
            location = obj.group.project.location
        else:
            # Entry in the forum
            location = obj.discussion.project.location
        if is_moderator(user, location):
            access = True
    return access


I set a path and a random name for the background image of the project .

def get_upload_path(instance, filename):
    """ I set a path and a random name for the background image of the project. """
    return 'img/projects/' + uuid4().hex + os.path.splitext(filename)[1]

def get_upload_path ( instance , filename ) : return 'img/projects/' + uuid4 ( ) . hex + os . path . splitext ( filename ) [ 1 ]


Same as above but for attachment files .

def get_attachment_path(instance, filename):
    """ Same as above, but for attachment files.
    """
    return 'attachments/' + uuid4().hex + os.path.splitext(filename)[1]

def get_attachment_path ( instance , filename ) : return 'attachments/' + uuid4 ( ) . hex + os . path . splitext ( filename ) [ 1 ]


Make sure that etherpad groups will be deleted along with project .

def cleanup(sender, instance, **kwargs):
    """ Make sure that etherpad groups will be deleted along with project. """
    instance.destroy()

def cleanup ( sender , instance , * * kwargs ) : instance . destroy ( )


Make sure that files will be deleted along with attachments .

def cleanup_attachment(sender, instance, **kwargs):
    """ Make sure that files will be deleted along with attachments.
    """
    instance.attachment.delete(save=False)

def cleanup_attachment ( sender , instance , * * kwargs ) : instance . attachment . delete ( save = False )


We assume that everyone may download file .

def get_attachment(request, pk):
    """ We assume that everyone may download file.
    """
    attachment = get_object_or_404(Attachment, pk=pk)
    types_to_open = ('application/pdf', 'application/msword', )
    if attachment.mime_type in types_to_open:
        with open(attachment.attachment.path) as pdf:
            response = HttpResponse(pdf.read(), content_type=attachment.mime_type)
            response['Content-Disposition'] = 'inline;filename={}'.format(
                attachment.attachment.name.split('/')[-1])
            return response
        pdf.closed
    else:
        response = HttpResponse(attachment.attachment.open(),
                            content_type=attachment.mime_type)
        response['Content-Disposition'] = 'attachment; filename=%s' % attachment
    return response

def
get_attachment
(
request
,
pk
)
:
attachment
=
get_object_or_404
(
Attachment
,
pk
=
pk
)
types_to_open
=
(
'application/pdf'
,
'application/msword'
,
)
if
attachment
.
mime_type
in
types_to_open
:
with
open
(
attachment
.
attachment
.
path
)
as
pdf
:
response
=
HttpResponse
(
pdf
.
read
(
)
,
content_type
=
attachment
.
mime_type
)
response
[
'Content-Disposition'
]
=
'inline;filename={}'
.
format
(
attachment
.
attachment
.
name
.
split
(
'/'
)
[
1
]
)
return
response
pdf
.
closed
else
:
response
=
HttpResponse
(
attachment
.
attachment
.
open
(
)
,
content_type
=
attachment
.
mime_type
)
response
[
'Content-Disposition'
]
=
'attachment; filename=%s'
%
attachment
return
response


We set an order for groups or tasks . The ID type of the content the ID of the object and the direction ( UP or DOWN ) need to be passed to the function .

def set_element_order(request, content_type, object_id, direction):
    """ We set an order for groups or tasks. The ID type of the content,
    the ID of the object and the direction ('UP' or 'DOWN') need to
    be passed to the function.
    """
    model = ContentType.objects.get(pk=content_type).model_class()
    obj = model.objects.get(pk=object_id)
    if direction == 'UP':
        obj.up()
    else:
        obj.down()
    if hasattr(obj, 'get_absolute_url'):
        # Task
        redirect_url = obj.get_absolute_url()
    else:
        # Group of tasks
        redirect_url = obj.project.get_absolute_url()
    return redirect(redirect_url)

def
set_element_order
(
request
,
content_type
,
object_id
,
direction
)
:
model
=
ContentType
.
objects
.
get
(
pk
=
content_type
)
.
model_class
(
)
obj
=
model
.
objects
.
get
(
pk
=
object_id
)
if
direction
==
'UP'
:
obj
.
up
(
)
else
:
obj
.
down
(
)
if
hasattr
(
obj
,
'get_absolute_url'
)
:
# Task
redirect_url
=
obj
.
get_absolute_url
(
)
else
:
# Group of tasks
redirect_url
=
obj
.
project
.
get_absolute_url
(
)
return
redirect
(
redirect_url
)


We change the status of the task to finished / unfinished

def toggle_task_state(request, pk):
    """ We change the status of the task to finished/unfinished """
    task = get_object_or_404(Task, pk=pk)
    if task.is_done:
        task.is_done = False
        message = _(u"Task marked as undone")
    else:
        task.is_done = True
        message = _(u"Task marked as finished")
    task.save()
    finished_task(request.user, task)
    if request.is_ajax():
        context = json.dumps(
            {'success': True,
             'is_done': task.is_done,
             'message': message, })
        return HttpResponse(context, content_type="application/json")
    return redirect(task.get_absolute_url())

def
toggle_task_state
(
request
,
pk
)
:
task
=
get_object_or_404
(
Task
,
pk
=
pk
)
if
task
.
is_done
:
task
.
is_done
=
False
message
=
_
(
u"Task marked as undone"
)
else
:
task
.
is_done
=
True
message
=
_
(
u"Task marked as finished"
)
task
.
save
(
)
finished_task
(
request
.
user
,
task
)
if
request
.
is_ajax
(
)
:
context
=
json
.
dumps
(
{
'success'
:
True
,
'is_done'
:
task
.
is_done
,
'message'
:
message
,
}
)
return
HttpResponse
(
context
,
content_type
=
"application/json"
)
return
redirect
(
task
.
get_absolute_url
(
)
)


Handy shortcut to get gallery object and validate id input .

def get_gallery(project, gallery_id):
    """ Handy shortcut to get gallery object and validate id input.
    """
    try:
        gallery_id = int(gallery_id)
    except (TypeError, ValueError):
        raise Http404
    try:
        return ContentObjectGallery.objects.for_object(project).get(pk=gallery_id)
    except ContentObjectGallery.DoesNotExist:
        raise Http404

def get_gallery ( project , gallery_id ) : try : gallery_id = int ( gallery_id ) except ( TypeError , ValueError ) : raise Http404 try : return ContentObjectGallery . objects . for_object ( project ) . get ( pk = gallery_id ) except ContentObjectGallery . DoesNotExist : raise Http404


def get_context_data(self, **kwargs):
        """ Serializes values for scripts. """
        serializer = MarkerSerializer(self.object.markers.all(), many=True,
                                      context={'request': self.request})
        context = super(VotingMapMixin, self).get_context_data(**kwargs)
        context.update({'markers': JSONRenderer().render(serializer.data)})
        return context

def get_context_data ( self , * * kwargs ) : serializer = MarkerSerializer ( self . object . markers . all ( ) , many = True , context = { 'request' : self . request } ) context = super ( VotingMapMixin , self ) . get_context_data ( * * kwargs ) context . update ( { 'markers' : JSONRenderer ( ) . render ( serializer . data ) } ) return context


TODO : for now we serve only . txt files . More formats required!

def form_valid(self, form):
        """ TODO: for now we serve only .txt files. More formats required! """
        pad = form.cleaned_data['pad']
        response = HttpResponse(pad.text, content_type='text/plain')
        response['Content-Disposition'] = "attachment; filename=%s.txt" % pad.pad_id
        return response

def
form_valid
(
self
,
form
)
:
pad
=
form
.
cleaned_data
[
'pad'
]
response
=
HttpResponse
(
pad
.
text
,
content_type
=
'text/plain'
)
response
[
'Content-Disposition'
]
=
"attachment; filename=%s.txt"
%
pad
.
pad_id
return
response


We have to be sure that pad name does not exist yet .

def clean(self):
        """ We have to be sure that pad name does not exist yet. """
        cleaned_data = super(PadCreationForm, self).clean()
        group = cleaned_data.get('group')
        pad_id = u"{}${}".format(group.etherpad_id,
            cleaned_data.get('name').replace(' ', '_'))
        client = EtherpadLiteClient(
            base_params={'apikey': settings.ETHERPAD_API_KEY},
            base_url=settings.ETHERPAD_INTERNAL_URL)
        response = client.listPads(groupID=group.etherpad_id)
        if pad_id in response['padIDs']:
            self.add_error('name', _(u"Name already exists"))
        return cleaned_data

def
clean
(
self
)
:
cleaned_data
=
super
(
PadCreationForm
,
self
)
.
clean
(
)
group
=
cleaned_data
.
get
(
'group'
)
pad_id
=
u"{}${}"
.
format
(
group
.
etherpad_id
,
cleaned_data
.
get
(
'name'
)
.
replace
(
' '
,
'_'
)
)
client
=
EtherpadLiteClient
(
base_params
=
{
'apikey'
:
settings
.
ETHERPAD_API_KEY
}
,
base_url
=
settings
.
ETHERPAD_INTERNAL_URL
)
response
=
client
.
listPads
(
groupID
=
group
.
etherpad_id
)
if
pad_id
in
response
[
'padIDs'
]
:
self
.
add_error
(
'name'
,
_
(
u"Name already exists"
)
)
return
cleaned_data


Make sure that groups and pads will be deleted in etherpad - lite db .

def cleanup_etherpad(sender, instance, **kwargs):
    """ Make sure that groups and pads will be deleted in etherpad-lite db. """
    try:
        instance.destroy()
    except EtherpadException:
        pass

def cleanup_etherpad ( sender , instance , * * kwargs ) : try : instance . destroy ( ) except EtherpadException : pass


Creates new ehterpad author instnce when user register .

def activate_user_author(sender, instance, **kwargs):
    """ Creates new ehterpad author instnce when user register. """
    try:
        author = EtherpadAuthor.objects.get(user=instance)
    except EtherpadAuthor.DoesNotExist:
        author = EtherpadAuthor.objects.create(user=instance)
    return author

def activate_user_author ( sender , instance , * * kwargs ) : try : author = EtherpadAuthor . objects . get ( user = instance ) except EtherpadAuthor . DoesNotExist : author = EtherpadAuthor . objects . create ( user = instance ) return author


Send test email to check if celery beat works .

def send_test_email(receiver, subject, title, msg):
    """ Send test email to check if celery beat works. """
    email = TestTemplateEmail()
    return email.send(receiver, {
        'subject': subject,
        'title': title,
        'msg': msg,
    })

def send_test_email ( receiver , subject , title , msg ) : email = TestTemplateEmail ( ) return email . send ( receiver , { 'subject' : subject , 'title' : title , 'msg' : msg , } )


Run this task once a day to find not finished registrations and delete register demand objects along with user objects created during process .

def cleanup_register_demands(forced=False):
    """
    Run this task once a day to find not finished registrations and delete
    register demand objects along with user objects created during process.
    """
    logger.info(u"[{}]: Started register cleanup".format(timezone.now()))
    now = timezone.now()
    for demand in RegisterDemand.objects.all():
        delta_t = now - demand.date
        if delta_t > datetime.timedelta(days=1) or forced:
            logger.info(u"Deleting user {} and register demand {}"\
                .format(demand.user.email, demand.pk))
            demand.user.delete()
            demand.delete()
    logger.info(u"[{}]: Finished register cleanup".format(timezone.now()))


def update_indexes():
    """ Update haystack indexes. """
    logger.info(u"[{}]: Started update indexes".format(timezone.now()))
    filename = os.path.join(settings.BASE_DIR, 'manage.py')
    logger.info(u"[{}]: Finished update indexes".format(timezone.now()))
    return subprocess.call(['python', filename, 'update_index'])

def
update_indexes
(
)
:
logger
.
info
(
u"[{}]: Started update indexes"
.
format
(
timezone
.
now
(
)
)
)
filename
=
os
.
path
.
join
(
settings
.
BASE_DIR
,
'manage.py'
)
logger
.
info
(
u"[{}]: Finished update indexes"
.
format
(
timezone
.
now
(
)
)
)
return
subprocess
.
call
(
[
'python'
,
filename
,
'update_index'
]
)


This function clears the cache saved in the template when we change the language of the site .

def flush_page_cache():
    """
    This function clears the cache saved in the template
    when we change the language of the site.
    """
    langs = [x[0] for x in settings.LANGUAGES]
    sections = ['home',]
    for l in langs:
        for s in sections:
            key = '_'.join([s, l])
            cache.delete(key)

def flush_page_cache ( ) : langs = [ x [ 0 ] for x in settings . LANGUAGES ] sections = [ 'home' , ] for l in langs : for s in sections : key = '_' . join ( [ s , l ] ) cache . delete ( key )


Returns current PROJECT version . This is useful for static files builds e . g . scripts and styles . This way we can append common suffx for all files as well as for HTML tags later .

def get_current_version():
    """ Returns current PROJECT version. This is useful for static files builds,
        e.g. scripts and styles. This way we can append common suffx for all files
        as well as for HTML tags later.
    """
    try:
        v_file = open(os.path.join(settings.BASE_DIR, 'VERSION'))
    except IOError:
        return '0.0.0'
    version = v_file.read().strip()
    v_file.close()
    return version

def get_current_version ( ) : try : v_file = open ( os . path . join ( settings . BASE_DIR , 'VERSION' ) ) except IOError : return '0.0.0' version = v_file . read ( ) . strip ( ) v_file . close ( ) return version


Changes the values of model fields on a serialized value . It takes a concrete instance of the model as an argument and returns a dictionary .

def serialize_item(model):
    """ Changes the values of model fields on a serialized value. It takes
    a concrete instance of the model as an argument and returns a dictionary. """
    model_dict = {}
    for key, val in model.__dict__.iteritems():
        if key.startswith('_'):
            continue
        elif type(model.__dict__[key]).__name__ == 'ImageFieldFile':
            model_dict.update({key: model.__dict__[key].path})
        elif type(model.__dict__[key]).__name__ == 'datetime':
            model_dict.update({key: model.__dict__[key].isoformat()})
        else:
            model_dict.update({key: model.serializable_value(key)})
    return model_dict

def serialize_item ( model ) : model_dict = { } for key , val in model . __dict__ . iteritems ( ) : if key . startswith ( '_' ) : continue elif type ( model . __dict__ [ key ] ) . __name__ == 'ImageFieldFile' : model_dict . update ( { key : model . __dict__ [ key ] . path } ) elif type ( model . __dict__ [ key ] ) . __name__ == 'datetime' : model_dict . update ( { key : model . __dict__ [ key ] . isoformat ( ) } ) else : model_dict . update ( { key : model . serializable_value ( key ) } ) return model_dict


Serializes all objects from a given model and returns the data in json format .

def serialize_models(model_class):
    """
    Serializes all objects from a given model and returns the data in json format.
    """
    model_data = []
    for item in model_class.objects.all():
        model_data.append(serialize_item(item))
    return json.dumps(model_data)

def serialize_models ( model_class ) : model_data = [ ] for item in model_class . objects . all ( ) : model_data . append ( serialize_item ( item ) ) return json . dumps ( model_data )


Exports all objects connected with a given model ( class objects need to be passed as a function parameter ) to a universal JSON format and saves the results in a file in a proper folder .

def export_items(model_class):
    """ Exports all objects connected with a given model (class objects need
    to be passed as a function parameter) to a universal JSON format and
    saves the results in a file in a proper folder. """
    dirname = os.path.join(DIR, model_class.__module__.split('.')[0].lower())
    if not os.path.isdir(dirname):
        os.mkdir(dirname)
    filename = os.path.join(dirname, model_class.__name__.lower() + '.json')
    f = codecs.open(filename, 'w+', 'utf-8')
    f.write(serialize_models(model_class))
    f.close()

def export_items ( model_class ) : dirname = os . path . join ( DIR , model_class . __module__ . split ( '.' ) [ 0 ] . lower ( ) ) if not os . path . isdir ( dirname ) : os . mkdir ( dirname ) filename = os . path . join ( dirname , model_class . __name__ . lower ( ) + '.json' ) f = codecs . open ( filename , 'w+' , 'utf-8' ) f . write ( serialize_models ( model_class ) ) f . close ( )


An example of a how the above function can be used in an idea . This function is written as once therefore it will not work without corrections .

def load_ideas():
    """ An example of a how the above function can be used in an idea. This
    function is written 'as once', therefore it will not work without
    corrections.
    """
    from django.contrib.auth.models import User
    from locations.models import Location
    from ideas.models import Idea
    from .helpers import date_from_iso
    f = codecs.open(os.path.join(DIR, 'ideas' ,'idea.json'), 'r', 'utf-8')
    data = json.loads(f.read())
    for l in data:
        try:
            idea = Idea.objects.create(
                creator = User.objects.get(pk=l['creator_id']),
                location = Location.objects.get(pk=l['location_id']),
                name = l['name'],
                description = l['description'],
                status = l['status'],
                date_created = date_from_iso(l['date_created']),
                date_edited = date_from_iso(l['date_edited'])
            )
        except Exception as ex:
            print ex.message

def load_ideas ( ) : from django . contrib . auth . models import User from locations . models import Location from ideas . models import Idea from . helpers import date_from_iso f = codecs . open ( os . path . join ( DIR , 'ideas' , 'idea.json' ) , 'r' , 'utf-8' ) data = json . loads ( f . read ( ) ) for l in data : try : idea = Idea . objects . create ( creator = User . objects . get ( pk = l [ 'creator_id' ] ) , location = Location . objects . get ( pk = l [ 'location_id' ] ) , name = l [ 'name' ] , description = l [ 'description' ] , status = l [ 'status' ] , date_created = date_from_iso ( l [ 'date_created' ] ) , date_edited = date_from_iso ( l [ 'date_edited' ] ) ) except Exception as ex : print ex . message


Used when the entire index for model is updated .

def index_queryset(self, using=None):
        """Used when the entire index for model is updated."""
        return self.get_model().objects.all()

def index_queryset ( self , using = None ) : return self . get_model ( ) . objects . all ( )


Update cached items for location set so there will be no delay between creating content object and displaying it .

def update_cached_items(sender, instance, created, **kwargs):
    """
    Update cached items for location set so there will be no delay between
    creating content object and displaying it.
    """
    postfix = None

    if isinstance(instance, News):
        postfix = 'news'
    elif isinstance(instance, Idea):
        postfix = 'ideas'
    elif isinstance(instance, Discussion):
        postfix = 'forum'
    elif isinstance(instance, Poll):
        postfix = 'polls'

    if postfix is None or not hasattr(instance, 'location'):
        return False

    key = "{}_{}_{}".format(instance.location.slug, get_language(), postfix)
    ct = ContentType.objects.get_for_model(instance)
    qs = ct.get_all_objects_for_this_type() \
        .filter(location__pk__in=instance.location.get_children_id_list())
    qs = ct.get_all_objects_for_this_type().filter(location=instance.location)

    redis_cache.set(key, qs)

def update_cached_items ( sender , instance , created , * * kwargs ) : postfix = None if isinstance ( instance , News ) : postfix = 'news' elif isinstance ( instance , Idea ) : postfix = 'ideas' elif isinstance ( instance , Discussion ) : postfix = 'forum' elif isinstance ( instance , Poll ) : postfix = 'polls' if postfix is None or not hasattr ( instance , 'location' ) : return False key = "{}_{}_{}" . format ( instance . location . slug , get_language ( ) , postfix ) ct = ContentType . objects . get_for_model ( instance ) qs = ct . get_all_objects_for_this_type ( ) . filter ( location__pk__in = instance . location . get_children_id_list ( ) ) qs = ct . get_all_objects_for_this_type ( ) . filter ( location = instance . location ) redis_cache . set ( key , qs )


Returns this form rendered as HTML <p>s.

Returns this form rendered as HTML <p > s .

def as_p(self):
        "Returns this form rendered as HTML
s."
        return self._html_output(
            normal_row = """

                %(label)s

%(field)s

""",
            error_row = '
%s
',
            row_ender = '
',
            help_text_html = '%s',
            errors_on_separate_row = True)

"""

                %(label)s

%(field)s

"""


A small helper that converts the day / year / month onto a datetime in python . Useful in all views with data filters .

def get_time_difference(period):
    """
    A small helper that converts the day/year/month onto a datetime in python.
    Useful in all views with data filters.
    """
    if period == 'day':
        time_delta = timezone.now() - datetime.timedelta(days=1)
    elif period == 'week':
        time_delta = timezone.now() - datetime.timedelta(days=7)
    elif period == 'month':
        time_delta = timezone.now() - relativedelta(months=1)
    elif period == 'year':
        time_delta = timezone.now() - relativedelta(years=1)
    else:
        time_delta = None
    return time_delta

def get_time_difference ( period ) : if period == 'day' : time_delta = timezone . now ( ) - datetime . timedelta ( days = 1 ) elif period == 'week' : time_delta = timezone . now ( ) - datetime . timedelta ( days = 7 ) elif period == 'month' : time_delta = timezone . now ( ) - relativedelta ( months = 1 ) elif period == 'year' : time_delta = timezone . now ( ) - relativedelta ( years = 1 ) else : time_delta = None return time_delta


Changes datetime . isoformat back to datetime object .

def date_from_iso(isodate):
    """ Changes datetime.isoformat back to datetime object."""
    return datetime.datetime.strptime(isodate[:-7],'%Y-%m-%dT%H:%M:%S.%f')

def date_from_iso ( isodate ) : return datetime . datetime . strptime ( isodate [ : - 7 ] , '%Y-%m-%dT%H:%M:%S.%f' )


Allow only whitelisted tags . I have changed this method slightly to allow defining tag whitelist in project s settings module .

def sanitizeHtml(value, base_url=None):
    """
    Allow only whitelisted tags. I have changed this method slightly to allow
    defining tag whitelist in project's settings module.

    @see: http://stackoverflow.com/questions/16861/sanitising-user-input-using-python/25136#25136
    """
    rjs = r'[\s]*(&#x.{1,7})?'.join(list('javascript:'))
    rvb = r'[\s]*(&#x.{1,7})?'.join(list('vbscript:'))
    re_scripts = re.compile('(%s)|(%s)' % (rjs, rvb), re.IGNORECASE)
    validTags = settings.VALID_TAGS
    validAttrs = settings.VALID_ATTRS
    urlAttrs = settings.URL_ATTRS
    soup = BeautifulSoup(value)
    for comment in soup.findAll(text=lambda text: isinstance(text, Comment)):
        # Get rid of comments
        comment.extract()
    for tag in soup.findAll(True):
        if tag.name not in validTags:
            tag.hidden = True
        attrs = tag.attrs
        tag.attrs = []
        for attr, val in attrs:
            if attr in validAttrs:
                val = re_scripts.sub('', val) # Remove scripts (vbs & js)
                if attr in urlAttrs:
                    val = urljoin(base_url, val) # Calculate the absolute url
                tag.attrs.append((attr, val))

    return soup.renderContents().decode('utf8')

def
sanitizeHtml
(
value
,
base_url
=
)
:
rjs
=
r'[\s]*(&#x.{1,7})?'
.
join
(
list
(
'javascript:'
)
)
rvb
=
r'[\s]*(&#x.{1,7})?'
.
join
(
list
(
'vbscript:'
)
)
re_scripts
=
re
.
compile
(
'(%s)|(%s)'
%
(
rjs
,
rvb
)
,
re
.
IGNORECASE
)
validTags
=
settings
.
VALID_TAGS
validAttrs
=
settings
.
VALID_ATTRS
urlAttrs
=
settings
.
URL_ATTRS
soup
=
BeautifulSoup
(
value
)
for
comment
in
soup
.
findAll
(
text
=
lambda
text
:
isinstance
(
text
,
Comment
)
)
:
# Get rid of comments
comment
.
extract
(
)
for
tag
in
soup
.
findAll
(
True
)
:
if
tag
.
name
not
in
validTags
:
tag
.
hidden
=
True
attrs
=
tag
.
attrs
tag
.
attrs
=
[
]
for
attr
,
val
in
attrs
:
if
attr
in
validAttrs
:
val
=
re_scripts
.
sub
(
''
,
val
)
# Remove scripts (vbs & js)
if
attr
in
urlAttrs
:
val
=
urljoin
(
base_url
,
val
)
# Calculate the absolute url
tag
.
attrs
.
append
(
(
attr
,
val
)
)
return
soup
.
renderContents
(
)
.
decode
(
'utf8'
)


Truncates a string after a given number of chars keeping whole words .

def truncatesmart(value, limit=40):
    """
    Truncates a string after a given number of chars keeping whole words.
    """
    try:
        limit = int(limit)
    # invalid literal for int()
    except ValueError:
        # Fail silently.
        return value

    # Make sure it's unicode
    value = unicode(value)

    # Return the string itself if length is smaller or equal to the limit
    if len(value) <= limit:
        return value

    # Cut the string
    value = value[:limit]

    # Break into words and remove the last
    words = value.split(' ')[:-1]

    # Join the words and return
    return ' '.join(words) + '...'


Truncate HTML string preserving tag structure and character entities .

def truncatehtml(string, length, ellipsis='...'):
    """Truncate HTML string, preserving tag structure and character entities."""
    length = int(length)
    output_length = 0
    i = 0
    pending_close_tags = {}

    while output_length < length and i < len(string):
        c = string[i]

        if c == '<':
            # probably some kind of tag
            if i in pending_close_tags:
                # just pop and skip if it's closing tag we already knew about
                i += len(pending_close_tags.pop(i))
            else:
                # else maybe add tag
                i += 1
                match = tag_end_re.match(string[i:])
                if match:
                    tag = match.groups()[0]
                    i += match.end()

                    # save the end tag for possible later use if there is one
                    match = re.search(r'(</' + tag + '[^>]*>)', string[i:], re.IGNORECASE)
                    if match:
                        pending_close_tags[i + match.start()] = match.groups()[0]
                else:
                    output_length += 1 # some kind of garbage, but count it in

        elif c == '&':
            # possible character entity, we need to skip it
            i += 1
            match = entity_end_re.match(string[i:])
            if match:
                i += match.end()

            # this is either a weird character or just '&', both count as 1
            output_length += 1
        else:
            # plain old characters

            skip_to = string.find('<', i, i + length)
            if skip_to == -1:
                skip_to = string.find('&', i, i + length)
            if skip_to == -1:
                skip_to = i + length

            # clamp
            delta = min(skip_to - i,
                        length - output_length,
                        len(string) - i)

            output_length += delta
            i += delta

    output = [string[:i]]
    if output_length == length:
        output.append(ellipsis)

    for k in sorted(pending_close_tags.keys()):
        output.append(pending_close_tags[k])

    return "".join(output)


A simple function that makes use of the PyICU library to sort search results alphabetically with UTF sings of a given language .

def sort_by_locale(queryset, key, language=None, reverse=False):
    """
    A simple function that makes use of the PyICU library to sort search
    results alphabetically with UTF sings of a given language.

    The 'queryset' parameter is compulsory, it can be both a Django queryset
    and a simple python list.

    The 'key' parameter is a function by which we will sort.

    The language parameter is also compulsory. It's the language code
    in ISO format (pl, en etc.) given as a string.
    """
    if language is None:
        language = translation.get_language()
    code = language.lower() + '_' + language.upper() + '.' + 'UTF-8'
    collator = icu.Collator.createInstance(icu.Locale(code))
    q = list(queryset)
    q.sort(key=key, cmp=collator.compare, reverse=reverse)
    return q

def sort_by_locale ( queryset , key , language = None , reverse = False ) : if language is None : language = translation . get_language ( ) code = language . lower ( ) + '_' + language . upper ( ) + '.' + 'UTF-8' collator = icu . Collator . createInstance ( icu . Locale ( code ) ) q = list ( queryset ) q . sort ( key = key , cmp = collator . compare , reverse = reverse ) return q


Scales image . This images are then used as background for location and profile pages .

def process_background_image(imgfile, dirname=None):
    """
    Scales image. This images are then used as background for location and
    profile pages.

    Function takes image file (usually from request.FILES['file']) as argument
    and changes it's size and name. If `dirname` is provided as path relative
    to MEDIA_ROOT settings, image will be saved on this path. If not ,`img`
    folder will be used instead.
    """
    img = Image.open(imgfile)
    pathname = dirname or 'img'
    dirname = os.path.join(settings.MEDIA_ROOT, pathname)
    imgname = str(uuid()) + str(len(os.listdir(dirname))) + '.jpg'
    if img.size[0] > settings.BACKGROUND_IMAGE_SIZE:
        img.thumbnail((settings.BACKGROUND_IMAGE_SIZE, settings.BACKGROUND_IMAGE_SIZE))
    img.save(os.path.join(dirname, imgname), 'JPEG')
    return File(open(os.path.join(dirname, imgname)))

def process_background_image ( imgfile , dirname = None ) : img = Image . open ( imgfile ) pathname = dirname or 'img' dirname = os . path . join ( settings . MEDIA_ROOT , pathname ) imgname = str ( uuid ( ) ) + str ( len ( os . listdir ( dirname ) ) ) + '.jpg' if img . size [ 0 ] > settings . BACKGROUND_IMAGE_SIZE : img . thumbnail ( ( settings . BACKGROUND_IMAGE_SIZE , settings . BACKGROUND_IMAGE_SIZE ) ) img . save ( os . path . join ( dirname , imgname ) , 'JPEG' ) return File ( open ( os . path . join ( dirname , imgname ) ) )


def ct_for_obj(obj):
    """
    Returns content type of given object.
    """
    if not obj:
        return None
    return ContentType.objects.get_for_model(obj)

def ct_for_obj ( obj ) : if not obj : return None return ContentType . objects . get_for_model ( obj )


Creates absolute url for site contents and appends given path .

def full_uri(request, path=""):
    """ Creates absolute url for site contents and appends given path.

    Args:
        request: Django request object
        path: path to append, usually reverse result
    Returns:
        String representing full absolute url
    """
    protocol = 'https' if request.is_secure else 'http'
    domain = Site.objects.get_current().domain
    return "{}://{}{}".format(protocol, domain, path)

def full_uri ( request , path = "" ) : protocol = 'https' if request . is_secure else 'http' domain = Site . objects . get_current ( ) . domain return "{}://{}{}" . format ( protocol , domain , path )


def page(self, page=None):
        """ Get single results page. """
        if not page: page = 1
        set_finish = int(page) * self.per_page
        return self.queryset[set_finish-self.per_page:set_finish]

def page ( self , page = None ) : if not page : page = 1 set_finish = int ( page ) * self . per_page return self . queryset [ set_finish - self . per_page : set_finish ]


def __init__(self, location):
        """ Prepare object list. """
        self.location = location
        self._items = {}
        self.news_list = self.location.news_set.all()
        self.idea_list = self.location.idea_set.all()
        self.poll_list = self.location.poll_set.all()

def __init__ ( self , location ) : self . location = location self . _items = { } self . news_list = self . location . news_set . all ( ) self . idea_list = self . location . idea_set . all ( ) self . poll_list = self . location . poll_set . all ( )


Returns items in few formats and different order .

def get_items(self, format=None, order=None):
        """ Returns items in few formats and different order. """
        items = self._items
        if order == 'count':
            items = sorted(items.items(), key=itemgetter(1))
        if format == 'json':
            return json.dumps(items)
        else:
            return items.iteritems()

def get_items ( self , format = None , order = None ) : items = self . _items if order == 'count' : items = sorted ( items . items ( ) , key = itemgetter ( 1 ) ) if format == 'json' : return json . dumps ( items ) else : return items . iteritems ( )


Count how many times given item was used .

def count_items(self, itm):
        """ Count how many times given item was used. """
        try:
            return self._items[itm]
        except KeyError:
            return 0

def count_items ( self , itm ) : try : return self . _items [ itm ] except KeyError : return 0


Get all tagged objects and prepare tag list .

def __init__(self, location):
        """ Get all tagged objects and prepare tag list. """
        super(TagFilter, self).__init__(location)
        self._filter_tags(self.news_list, self.idea_list, self.poll_list)

def __init__ ( self , location ) : super ( TagFilter , self ) . __init__ ( location ) self . _filter_tags ( self . news_list , self . idea_list , self . poll_list )


Prepare dict containing tags in location and their counter .

def _filter_tags(self, *args):
        """ Prepare dict containing tags in location and their counter. """
        itemlist = []
        for arg in args:
            itemlist += arg
        for itm in itemlist:
            for tag in itm.tags.all():
                if tag.name and len(tag.name) > 0:
                    try:
                        self._items[tag.name] += 1
                    except KeyError:
                        self._items[tag.name] = 1

def _filter_tags ( self , * args ) : itemlist = [ ] for arg in args : itemlist += arg for itm in itemlist : for tag in itm . tags . all ( ) : if tag . name and len ( tag . name ) > 0 : try : self . _items [ tag . name ] += 1 except KeyError : self . _items [ tag . name ] = 1


This function check whether the user is a mod of a given location . Returns True also for superadmins and supermoderators .

def is_moderator(user, location):
    """
    This function check whether the user is a mod of
    a given location. Returns 'True' also for superadmins
    and 'supermoderators'.
    """
    if user.is_superuser:
        return True

    # The group name is written "non-dynamically" !!!
    if 'Editor' in [x.name for x in Group.objects.all()]:
        return True

    if user.is_anonymous():
        return False

    profile = UserProfile.objects.get(user = user)
    if profile.mod_areas.filter(pk=location.pk).exists():
        return True

    return False


Action hook for activity stream when new place is created TODO - move it to more appropriate place .

def create_place_action_hook(sender, instance, created, **kwargs):
    """
    Action hook for activity stream when new place is created
    TODO - move it to more appropriate place.
    """
    if created:
        action.send(instance.creator, action_object=instance, verb=_('created'))
        instance.users.add(instance.creator)
        instance.creator.profile.mod_areas.add(instance)
        follow(instance.creator, instance, actor_only=False, send_action=False)

def create_place_action_hook ( sender , instance , created , * * kwargs ) : if created : action . send ( instance . creator , action_object = instance , verb = _ ( 'created' ) ) instance . users . add ( instance . creator ) instance . creator . profile . mod_areas . add ( instance ) follow ( instance . creator , instance , actor_only = False , send_action = False )


Inform about the creation of a new object . It is possible to plugg evocation of this idea for each object that has the field creator and location - i . e . for modules that inherit from a location .

def create_object_action_hook(sender, instance, created, **kwargs):
    """
    Inform about the creation of a new object. It is possible
    to plugg evocation of this idea for each object that has the field
    'creator' and 'location' - i.e. for modules that inherit from a location.
    """
    if not created:
        return True
    prof = UserProfile.objects.get(user=instance.creator)
    prof.rank_pts += 5
    prof.save()
    action.send(
        instance.creator,
        action_object = instance,
        verb = _(u"created"),
        target = instance.location
    )
    # This way author will be notified about comments etc.
    follow(instance.creator, instance, send_action=False)