Source code for corehq.apps.userreports.expressions.date_specs

import calendar
import datetime

from dateutil.relativedelta import relativedelta
from jsonobject.base_properties import DefaultProperty
from corehq.apps.userreports.transforms.custom.date import get_ethiopian_to_gregorian, get_gregorian_to_ethiopian

from dimagi.ext.jsonobject import JsonObject

from corehq.apps.userreports.expressions.getters import (
    transform_date,
    transform_int,
    transform_datetime)
from corehq.apps.userreports.specs import TypeProperty


[docs]class AddDaysExpressionSpec(JsonObject): """ Below is a simple example that demonstrates the structure. The expression below will add 28 days to a property called "dob". The date_expression and count_expression can be any valid expressions, or simply constants. .. code:: json { "type": "add_days", "date_expression": { "type": "property_name", "property_name": "dob", }, "count_expression": 28 } """ type = TypeProperty('add_days') date_expression = DefaultProperty(required=True) count_expression = DefaultProperty(required=True) def configure(self, date_expression, count_expression): self._date_expression = date_expression self._count_expression = count_expression def __call__(self, item, evaluation_context=None): date_val = transform_date(self._date_expression(item, evaluation_context)) int_val = transform_int(self._count_expression(item, evaluation_context)) if date_val is not None and int_val is not None: return date_val + datetime.timedelta(days=int_val) return None def __str__(self): return "add_day({date}, {count})".format( date=self._date_expression, count=self._count_expression)
[docs]class AddHoursExpressionSpec(JsonObject): """ Below is a simple example that demonstrates the structure. The expression below will add 12 hours to a property called "visit_date". The date_expression and count_expression can be any valid expressions, or simply constants. .. code:: json { "type": "add_hours", "date_expression": { "type": "property_name", "property_name": "visit_date", }, "count_expression": 12 } """ type = TypeProperty('add_hours') date_expression = DefaultProperty(required=True) count_expression = DefaultProperty(required=True) def configure(self, date_expression, count_expression): self._date_expression = date_expression self._count_expression = count_expression def __call__(self, item, evaluation_context=None): date_val = transform_datetime(self._date_expression(item, evaluation_context)) int_val = transform_int(self._count_expression(item, evaluation_context)) if date_val is not None and int_val is not None: return date_val + datetime.timedelta(hours=int_val) return None def __str__(self): return "add_day({date}, {count})".format( date=self._date_expression, count=self._count_expression)
[docs]class AddMonthsExpressionSpec(JsonObject): """ ``add_months`` offsets given date by given number of calendar months. If offset results in an invalid day (for e.g. Feb 30, April 31), the day of resulting date will be adjusted to last day of the resulting calendar month. The date_expression and months_expression can be any valid expressions, or simply constants, including negative numbers. .. code:: json { "type": "add_months", "date_expression": { "type": "property_name", "property_name": "dob", }, "months_expression": 28 } """ type = TypeProperty('add_months') date_expression = DefaultProperty(required=True) months_expression = DefaultProperty(required=True) def configure(self, date_expression, months_expression): self._date_expression = date_expression self._months_expression = months_expression def __call__(self, item, evaluation_context=None): date_val = transform_date(self._date_expression(item, evaluation_context)) months_count_val = transform_int(self._months_expression(item, evaluation_context)) if date_val is not None and months_count_val is not None: return date_val + relativedelta(months=months_count_val) return None def __str__(self): return "add_month({date}, {month})".format( date=self._date_expression, month=self._months_expression)
[docs]class MonthStartDateExpressionSpec(JsonObject): """ ``month_start_date`` returns date of first day in the month of given date and ``month_end_date`` returns date of last day in the month of given date. The ``date_expression`` can be any valid expression, or simply constant .. code:: json { "type": "month_start_date", "date_expression": { "type": "property_name", "property_name": "dob", }, } """ type = TypeProperty('month_start_date') date_expression = DefaultProperty(required=True) def configure(self, date_expression): self._date_expression = date_expression def __call__(self, item, evaluation_context=None): date_val = transform_date(self._date_expression(item, evaluation_context)) if date_val is not None: return datetime.date(date_val.year, date_val.month, 1) return None def __str__(self): return "first_day({})".format(self._date_expression)
class MonthEndDateExpressionSpec(JsonObject): type = TypeProperty('month_end_date') date_expression = DefaultProperty(required=True) def configure(self, date_expression): self._date_expression = date_expression def __call__(self, item, evaluation_context=None): date_val = transform_date(self._date_expression(item, evaluation_context)) if date_val is not None: first_week_day, last_day = calendar.monthrange(date_val.year, date_val.month) return datetime.date(date_val.year, date_val.month, last_day) return None def __str__(self): return "last_day({})".format(self._date_expression)
[docs]class DiffDaysExpressionSpec(JsonObject): """ ``diff_days`` returns number of days between dates specified by ``from_date_expression`` and ``to_date_expression``. The from_date_expression and to_date_expression can be any valid expressions, or simply constants. .. code:: json { "type": "diff_days", "from_date_expression": { "type": "property_name", "property_name": "dob", }, "to_date_expression": "2016-02-01" } """ type = TypeProperty('diff_days') from_date_expression = DefaultProperty(required=True) to_date_expression = DefaultProperty(required=True) def configure(self, from_date_expression, to_date_expression): self._from_date_expression = from_date_expression self._to_date_expression = to_date_expression def __call__(self, item, evaluation_context=None): from_date_val = transform_date(self._from_date_expression(item, evaluation_context)) to_date_val = transform_date(self._to_date_expression(item, evaluation_context)) if from_date_val is not None and to_date_val is not None: return (to_date_val - from_date_val).days return None def __str__(self): return "({}) - ({})".format(self._to_date_expression, self._from_date_expression)
def _datetime_now(): return datetime.datetime.utcnow() class UTCNow(JsonObject): type = TypeProperty('utcnow') def __call__(self, item, evaluation_context=None): return _datetime_now() def __str__(self): return "utcnow" class GregorianDateToEthiopianDateSpec(JsonObject): """ ``gregorian_date_to_ethiopian_date`` returns the ethiopian date for the gregorian date specified by ``date_expression``. The date_expression can be any valid expression, or simply a constant. .. code:: json { "type": "gregorian_date_to_ethiopian_date", "date_expression": { "type": "property_name", "property_name": "dob" } } """ class Meta(object): # prevent JsonObject from auto-converting dates etc. string_conversions = () type = TypeProperty("gregorian_date_to_ethiopian_date") date_expression = DefaultProperty(required=True) def configure(self, date_expression): self._date_expression = date_expression def __call__(self, item, evaluation_context=None): date_val = transform_date(self._date_expression(item, evaluation_context)) return get_gregorian_to_ethiopian(date_val) class EthiopianDateToGregorianDateSpec(JsonObject): """ ``ethiopian_date_to_gregorian_date`` returns the gregorian date for the ethiopian date specified by ``date_expression``. The date_expression can be any valid expression, or simply a constant. .. code:: json { "type": "ethiopian_date_to_gregorian_date", "date_expression": { "type": "property_name", "property_name": "dob" } } """ class Meta(object): # prevent JsonObject from auto-converting dates etc. string_conversions = () type = TypeProperty("ethiopian_date_to_gregorian_date") date_expression = DefaultProperty(required=True) def configure(self, date_expression): self._date_expression = date_expression def __call__(self, item, evaluation_context=None): unwrapped_date = self._date_expression(item, evaluation_context) if isinstance(unwrapped_date, (datetime.datetime, datetime.date)): unwrapped_date = unwrapped_date.strftime('%Y-%m-%d') return transform_date(get_ethiopian_to_gregorian(unwrapped_date))