Utilities

Cache

Clear cache

The function clear_cache() wraps all the functionality needed to totally empty the django cache. Especially convenient, if you put this function inside a management command, so you can flush the cache easily from the command line:

# my_app/management/commands/clear_cache.py

from django.core.management.base import BaseCommand
from ai_django_core.utils import clear_cache


class Command(BaseCommand):
    def handle(self, *args, **options):
        clear_cache()

Date

DateHelper

The date helper class provides constants to use when querying for weekdays (“Monday”, “Tuesday”) with the django ORM. The assignment from numbers to weekdays is not standarised throughout the different django packages. For example, in the calendar extension, Monday equals 0, in the djang ORM, a Monday is represented by 2.

To avoid using the integers directly, you can use the constants from this class as follows:

# Just get records where `my_date` is on a Sunday
MyModel.objects.filter(my_date__week_day=DateHelper.ORM_SUNDAY)

# Leave out records where `my_date` is on the weekend
MyModel.objects.exclude(my_date__week_day__in[DateHelper.ORM_SATURDAY, DateHelper.ORM_SUNDAY])

Add months

The function add_months(source_date) provides a simple way to add any number of months to a given date:

from ai_django_core.utils import add_months

new_date = add_months(datetime.date(year=2020, month=9, day=19), 2)
# new_date = datetime.date(2020, 11, 19)

You can also use a negative number to subtract months.

Add days

The function add_days(source_date) provides a simple way to add any number of months to a given date:

from ai_django_core.utils import add_days

new_date = add_days(datetime.date(year=2020, month=9, day=19), 2)
# new_date = datetime.date(2020, 9, 21)

You can also use a negative number to subtract days.

Add minutes

The function add_minutes(source_datetime) provides a simple way to add any number of months to a given date:

from ai_django_core.utils import add_days

new_datetime = add_minutes(datetime.datetime(year=2020, month=9, day=19, hour=8), 60)
# new_datetime = add_minutes.date(2020, 9, 19, 9)

You can also use a negative number to subtract minutes.

Get next month

The function get_next_month() is a wrapper for add_months() and will return the current day one month later.

First day of month

The function first_day_of_month(source_date) will return the first of month for any given date:

from ai_django_core.utils import first_day_of_month

new_date = first_day_of_month(datetime.date(year=2020, month=9, day=19))
# new_date = datetime.date(2020, 9, 1)

Format date to German format

The function get_formatted_date_str(source_date) will return the string representation in the German format (“d.m.Y”):

from ai_django_core.utils import get_formatted_date_str

date_str = get_formatted_date_str(datetime.date(year=2020, month=9, day=19))
# date_str = "19.09.2020"

Create string representation of seconds

The function get_time_from_seconds(seconds) will create a string representation of any (positive) number of seconds in the format “HH:mm:ss”:

from ai_django_core.utils import get_formatted_date_str

time_str = get_time_from_seconds(3661)
# time_str = "01:01:01"

time_str = get_time_from_seconds(65)
# time_str = "00:01:05"

Format datetime objects timezone-aware

The function datetime_format(target_datetime, dt_format) formats the given datetime according to the given format with strftime but takes the django timezone settings into account. If the timeszone cannot be interpreted, a fallback without the timezone is used:

# settings.py
TIME_ZONE = 'Europe/Berlin'

# my_code.py
source_date = datetime.datetime(year=2020, month=6, day=26, hour=8, tzinfo=pytz.UTC)
datetime_str = datetime_format(source_date, '%d.%m.%Y %H:%M')  # will return '26.06.2020 10:00'

Get start and end dates from calendar week

The function get_start_and_end_date_from_calendar_week(year, calendar_week) provides a simple way to get the Monday and Sunday of a given calendar week:

from ai_django_core.utils import get_start_and_end_date_from_calendar_week

monday, sunday = get_start_and_end_date_from_calendar_week(2020, 38)
# monday = datetime.date(2020, 09, 14); sunday = datetime.date(2020, 9, 20)

Get next calendar week

The function get_next_calendar_week(compare_date) will return the the calendar week following the week of the given compare_date as an integer.

import datetime
from ai_django_core.utils import get_next_calendar_week

next_calendar_week = get_next_calendar_week(datetime.date(year=2020, month=9, day=19))
# next_calendar_week = 39

Get next weekday

The function next_weekday(given_date, weekday) will return a date object of the next weekday following given_date.

import calendar
import datetime
from ai_django_core.utils import next_weekday

next_friday = next_weekday(datetime.date(year=2020, month=9, day=19), calendar.FRIDAY)
# next_friday = datetime.date(year=2020, month=9, day=25)

Calculate the exact delta between to dates in months

The function date_month_delta(start_date, end_date) calculates the number of months lying between two dates as float. So from April 15th to May 1st it’s 0.5 months. Attention: The end_date will be excluded in the result (outer border).

import calendar
import datetime
from ai_django_core.utils import date_month_delta

months = date_month_delta(datetime.date(year=2020, month=8, day=1), datetime.date(year=2020, month=10, day=1))
# months = 2.0

Get current date

The function tz_today() will return the current date as an object. If timezone-awareness is enabled, the date take it into consideration.

You can optionally use the argument str_format, then the current date will be formatted in the given way and the function will return a string. Please provide a strftime-compatible value.

from ai_django_core.utils import next_weekday

# Here we'll get an object
current_date = tz_today()

# Here we'll get a string
current_date = tz_today('%d.%m.%Y')

Object ownership helper

The function log_whodid provides a simple way to ensure object ownership is set correctly. Imagine, you have a model which is derived from CommonInfo:

from ai_django_core.models import CommonInfo

class MyModel(CommonInfo):
    pass

You can now use the helper in any place where you have the user object available to set created_by and lastmodified_by like this:

def my_view(request, pk):
    obj = MyModel.objects.get(pk=pk)
    # Let the magic happen
    log_whodid(obj, request.user)
    ...

Math

Round to decimal

The helper function round_to_decimal(value, precision) will round a given value to a specific precision, for example *.5. So 5.4 will be rounded to 5.5, and 5.6 to 5.5 as well. The result is always a float.

result = round_to_decimal(5.4, 0.5)
# result = 5.5

result = round_to_decimal(5.6, 0.5)
# result = 5.5

Round up decimal

The helper function round_up_decimal(value, precision) will round a given value up to a specific precision, for example *.5. So 5.4 will be rounded to 5.5, and 5.6 to 6 as well. The result is always a float.

result = round_up_decimal(5.4, 0.5)
# result = 5.5

result = round_up_decimal(5.6, 0.5)
# result = 6.0

Model

Convert object to dictionary

The function object_to_dict(obj, blacklisted_fields, include_id) takes an instance of a django model and extracts all attributes into a dictionary:

from django.db import models
class MyModel(models.Model):
    value1 = models.IntegerField()
    value2 = models.IntegerField()

    ....

obj = MyModel.objects.create(value_1=19, value_2=9)
result = object_to_dict(obj)
# result = {'value_1': 19, 'value_2': 9}

Optionally, fields can be excluded with the parameter blacklisted_fields. Passing a list of field names as string will prevent them from ending up in the result dictionary.

obj = MyModel.objects.create(value_1=19, value_2=9)
result = object_to_dict(obj, ['value_2'])
# result = {'value_1': 19}

By default the model ID is not part of the result. If you want to change this, pass include_id=True to the function:

obj = MyModel.objects.create(value_1=19, value_2=9)
result = object_to_dict(obj, include_id=True)
# result = {'id': 1, value_1': 19, 'value_2': 9}

Named tuple

Named tuple helper

General

Factory function for quickly making a namedtuple suitable for use in a Django model as a choices attribute on a field. It will preserve order.

Here you’ll find a basic example:

class MyModel(models.Model):
    COLORS = get_namedtuple_choices('COLORS', (
        (0, 'black', 'Black'),
        (1, 'white', 'White'),
    ))
    colors = models.PositiveIntegerField(choices=COLORS)

>>> MyModel.COLORS.black
0
>>> MyModel.COLORS.get_choices()
[(0, 'Black'), (1, 'White')]

class OtherModel(models.Model):
    GRADES = get_namedtuple_choices('GRADES', (
        ('FR', 'fr', 'Freshman'),
        ('SR', 'sr', 'Senior'),
    ))
    grade = models.CharField(max_length=2, choices=GRADES)

>>> OtherModel.GRADES.fr
'FR'
>>> OtherModel.GRADES.get_choices()
[('fr', 'Freshman'), ('sr', 'Senior')]

Helpers

# get_choices()
>>> MyModel.COLORS.get_choices()
[(0, 'Black'), (1, 'White')]

# get_choices_dict()
>>> MyModel.COLORS.get_choices_dict()
OrderedDict([(1, 'Black'), (2, 'White')])

# get_all()
>>> lambda  x: (print(color) for color in MyModel.COLORS.get_all())
(1, 'black', 'Black')
(2, 'white', 'White')

# get_choices_tuple()
>>> MyModel.COLORS.get_choices_tuple()
((1, 'black', 'Black'), (2, 'white', 'White'))

# get_values()
>>> MyModel.COLORS.get_values()
[1, 2]

# get_value_by_name()
>>> MyModel.COLORS.get_value_by_name('white')
2

# get_desc_by_value()
>>> MyModel.COLORS.get_desc_by_value(1)
Black

# get_name_by_value()
>>> MyModel.COLORS.get_name_by_value(1)
black

# is_valid()
>>> MyModel.COLORS.is_valid(1)
True

Get value from tuple by key

If you have a tuple my_tuple and you want to get the value for the key my_key, you can use the straight-forward helper function get_value_from_tuple_by_key():

my_tuple = (
    1, 'Value One',
    2, 'Value Two',
)

my_value = get_value_from_tuple_by_key(my_tuple, 1)
# my_value = 'Value One'

Get key from tuple by value

If you have a tuple my_tuple and you want to get the key for the value my_key, you can use the straight-forward helper function get_key_from_tuple_by_value():

my_tuple = (
    1, 'Value One',
    2, 'Value Two',
)

my_value = get_key_from_tuple_by_value(my_tuple, 'Value One')
# my_value = 1

String converter and utilities

Remove duplicates from list

Returns a list of unique entries from not_distinct_list.

from ai_django_core.utils.string import distinct

not_distinct_list = ['Beer', 'Wine', 'Whiskey', 'Beer']
distinct_list = distinct(not_distinct_list)

# Result: ['Whiskey', 'Wine', 'Beer']

Note that the order might change due to internal casting to a set.

Slugify a filename

Turns a string into a nice slugified filename.

from ai_django_core.utils.string import slugify_file_name

filename = 'hola and hello.txt'
slug = slugify_file_name(filename)
# Result: "hola_and_hello.txt"

You can pass a parameter to control how many characters the slug is supposed to have.

from ai_django_core.utils.string import slugify_file_name

filename = 'a very long filename.txt'
slug = slugify_file_name(filename, 6)
# Result: "a_very.txt"

Smart truncate

This helper cuts a string at a word-boundary and therefore keeps words intact. Similar to djangos default-filter truncatechars, you can pass the desired string length.

from ai_django_core.utils.string import smart_truncate

my_sentence = 'I am a very interesting sentence.'
truncated_str = smart_truncate(my_sentence, 10)
# Result: "I am a..."

By default, after cutting the string, it will append “…” which can be configured as follows:

from ai_django_core.utils.string import smart_truncate

my_sentence = 'I am a very interesting sentence.'
truncated_str = smart_truncate(my_sentence, 10, '[...]')
# Result: "I am a[...]"

Converting a float to a German-formatted string

If you have a float which you like to convert to a properly formatted string (German format), then you can do this:

from ai_django_core.utils.string import float_to_string

float_str = float_to_string(1234.56)
# Result: "1234,56"

If you are not sure your float value will always be set, you can either use the default fallback or overwrite it with something custom.

from ai_django_core.utils.string import float_to_string

value = None

# Default fallback
fallback_result = float_to_string(value)
# Result: "0,00"

# Custom fallback
fallback_result = float_to_string(value, 'NaN')
# Result: "NaN"

Converting a date object to string

If you want to easily convert a date object to a string in the given format, just use this helper:

import datetime
from ai_django_core.utils.string import date_to_string

# Default format (German)
date_str = date_to_string(datetime.date(2020, 9, 19))
# Result: "19.09.1985"

# Custom format
date_str = date_to_string(datetime.date(2020, 9, 19), str_format='%Y-%m-%d')
# Result: "2020-09-19"

Again, you can set a replacement if the date object is None:

import datetime
from ai_django_core.utils.string import date_to_string

# Default fallback
date_str = date_to_string(None)
# Result: "-"

# Custom fallback
date_str = date_to_string(None, 'no date')
# Result: "no date"

Converting a datetime object to string

If you want to easily convert a datetime object to a string in the given format, just use this helper:

import datetime
from ai_django_core.utils.string import datetime_to_string

# Default format (German)
datetime_str = datetime_to_string(datetime.datetime(2020, 9, 19, 8))
# Result: "19.09.1985"

# Custom format
datetime_str = datetime_to_string(datetime.datetime(2020, 9, 19, 8), str_format='%Y-%m-%d')
# Result: "2020-09-19"

Again, you can set a replacement if the datetime object is None:

import datetime
from ai_django_core.utils.string import datetime_to_string

# Default fallback
datetime_str = datetime_to_string(None)
# Result: "-"

# Custom fallback
datetime_str = datetime_to_string(None, 'no datetime')
# Result: "no datetime"

Converting numbers to string

If you have a float or int variable which you like to convert to a properly formatted string, then you can do this:

from ai_django_core.utils.string import number_to_string

number_str = number_to_string(1234.56, decimal_digits=2)
# Result: "1,234.56"

If you are not sure your float value will always be set, you can either use the default fallback or overwrite it with something custom.

from ai_django_core.utils.string import number_to_string

value = None

# Default fallback
fallback_result = number_to_string(value)
# Result: "0,00"

# Custom fallback
fallback_result = number_to_string(value, replacement='NaN')
# Result: "NaN"

Furthermore, all values will be rounded to the number of decimal_digits passed. Defaults to 0. Note, that the result will NOT be localised!

Getting a value or a default

Similar to django filter default, this method will return the value if it is not equal to None and will return the fallback value otherwise.

from ai_django_core.utils.string import string_or_none_to_string

value = 'I am a string.'

# Value set
result = string_or_none_to_string(value)
# Result: "I am a string."

# Value not set, default fallback value
result = string_or_none_to_string(None)
# Result: "-"

# Value not set, custom fallback value
result = string_or_none_to_string(None, replacement='empty')
# Result: "empty"

Lightweight XML to HTML converter

If you want a simple transformer from XML to HTML(-entities), you can use this helper:

from ai_django_core.utils.string import encode_to_xml

value = '<tag>Something with an ampersand (&)</tag>'
result = encode_to_xml(value)
# Result: "'&lt;tag&gt;Something with an ampersand (&amp;)&lt;/tag&gt;'"

This method will replace all “<”, “>” and “&” with their HTML representation.