mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-01-16 22:16:40 +01:00
306 lines
11 KiB
Python
306 lines
11 KiB
Python
import datetime
|
|
|
|
from apps.common.functions.format import get_format
|
|
from apps.common.utils.django import (
|
|
django_to_airdatepicker_datetime,
|
|
django_to_airdatepicker_datetime_separated,
|
|
django_to_python_datetime,
|
|
)
|
|
from django.forms import widgets
|
|
from django.utils import dates, formats, translation
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
|
|
class AirDatePickerInput(widgets.DateInput):
|
|
def __init__(
|
|
self,
|
|
attrs=None,
|
|
format=None,
|
|
clear_button=True,
|
|
auto_close=True,
|
|
read_only=True,
|
|
toggle_selected=None,
|
|
*args,
|
|
**kwargs,
|
|
):
|
|
attrs = attrs or {}
|
|
super().__init__(attrs=attrs, format=format, *args, **kwargs)
|
|
self.clear_button = clear_button
|
|
self.auto_close = auto_close
|
|
self.read_only = read_only
|
|
self.toggle_selected = (
|
|
toggle_selected if isinstance(toggle_selected, bool) else self.clear_button
|
|
)
|
|
|
|
@staticmethod
|
|
def _get_current_language():
|
|
"""Get current language code in format compatible with AirDatepicker"""
|
|
lang_code = translation.get_language()
|
|
# AirDatepicker uses simple language codes, except for pt-br
|
|
if lang_code.lower() == "pt-br":
|
|
return "pt-BR"
|
|
return lang_code.split("-")[0]
|
|
|
|
def _get_format(self):
|
|
"""Get the format string based on user settings or default"""
|
|
if self.format:
|
|
return self.format
|
|
|
|
return get_format("SHORT_DATE_FORMAT", use_l10n=True)
|
|
|
|
def build_attrs(self, base_attrs, extra_attrs=None):
|
|
attrs = super().build_attrs(base_attrs, extra_attrs)
|
|
|
|
attrs["class"] = attrs.get("class", "") + " input"
|
|
|
|
attrs["data-now-button-txt"] = _("Today")
|
|
attrs["data-auto-close"] = str(self.auto_close).lower()
|
|
attrs["data-clear-button"] = str(self.clear_button).lower()
|
|
attrs["data-toggle-selected"] = str(self.toggle_selected).lower()
|
|
attrs["data-language"] = self._get_current_language()
|
|
attrs["data-date-format"] = django_to_airdatepicker_datetime(self._get_format())
|
|
|
|
if self.read_only:
|
|
attrs["readonly"] = True
|
|
|
|
return attrs
|
|
|
|
def format_value(self, value):
|
|
"""Format the value for display in the widget."""
|
|
if value:
|
|
self.attrs["data-value"] = value
|
|
|
|
if value is None:
|
|
return ""
|
|
if isinstance(value, (datetime.date, datetime.datetime)):
|
|
return formats.date_format(value, format=self._get_format(), use_l10n=True)
|
|
|
|
return str(value)
|
|
|
|
def value_from_datadict(self, data, files, name):
|
|
"""Parse the datetime string from the form data."""
|
|
value = super().value_from_datadict(data, files, name)
|
|
if value:
|
|
try:
|
|
# This does it's best to convert Django's PHP-Style date format to a datetime format and reformat the
|
|
# value to be read by Django. Probably could be improved
|
|
return datetime.datetime.strptime(
|
|
value.strip(),
|
|
django_to_python_datetime(self._get_format())
|
|
or django_to_python_datetime(get_format("SHORT_DATETIME_FORMAT")),
|
|
).strftime("%Y-%m-%d")
|
|
except (ValueError, TypeError) as e:
|
|
return value
|
|
return None
|
|
|
|
|
|
class AirDateTimePickerInput(widgets.DateTimeInput):
|
|
def __init__(
|
|
self,
|
|
attrs=None,
|
|
format=None,
|
|
timepicker=True,
|
|
clear_button=True,
|
|
auto_close=True,
|
|
read_only=True,
|
|
toggle_selected=None,
|
|
*args,
|
|
**kwargs,
|
|
):
|
|
attrs = attrs or {}
|
|
super().__init__(attrs=attrs, format=format, *args, **kwargs)
|
|
self.timepicker = timepicker
|
|
self.clear_button = clear_button
|
|
self.auto_close = auto_close
|
|
self.read_only = read_only
|
|
self.toggle_selected = (
|
|
toggle_selected if isinstance(toggle_selected, bool) else self.clear_button
|
|
)
|
|
|
|
@staticmethod
|
|
def _get_current_language():
|
|
"""Get current language code in format compatible with AirDatepicker"""
|
|
lang_code = translation.get_language()
|
|
# AirDatepicker uses simple language codes
|
|
return lang_code.split("-")[0]
|
|
|
|
def _get_format(self):
|
|
"""Get the format string based on user settings or default"""
|
|
if self.format:
|
|
return self.format
|
|
|
|
return get_format("SHORT_DATETIME_FORMAT", use_l10n=True)
|
|
|
|
def build_attrs(self, base_attrs, extra_attrs=None):
|
|
attrs = super().build_attrs(base_attrs, extra_attrs)
|
|
|
|
date_format, time_format = django_to_airdatepicker_datetime_separated(
|
|
self._get_format()
|
|
)
|
|
|
|
# Add data attributes for AirDatepicker configuration
|
|
attrs["data-now-button-txt"] = _("Now")
|
|
attrs["data-timepicker"] = str(self.timepicker).lower()
|
|
attrs["data-auto-close"] = str(self.auto_close).lower()
|
|
attrs["data-toggle-selected"] = str(self.toggle_selected).lower()
|
|
attrs["data-clear-button"] = str(self.clear_button).lower()
|
|
attrs["data-language"] = self._get_current_language()
|
|
attrs["data-date-format"] = date_format
|
|
attrs["data-time-format"] = time_format
|
|
|
|
if self.read_only:
|
|
attrs["readonly"] = True
|
|
|
|
return attrs
|
|
|
|
def format_value(self, value):
|
|
"""Format the value for display in the widget."""
|
|
if value and isinstance(value, (datetime.date, datetime.datetime)):
|
|
self.attrs["data-value"] = datetime.datetime.strftime(
|
|
value, "%Y-%m-%dT%H:%M:00"
|
|
)
|
|
elif value and isinstance(value, str):
|
|
value = datetime.datetime.strptime(value, "%Y-%m-%d %H:%M:00")
|
|
self.attrs["data-value"] = datetime.datetime.strftime(
|
|
value, "%Y-%m-%dT%H:%M:00"
|
|
)
|
|
|
|
if value is None:
|
|
return ""
|
|
if isinstance(value, (datetime.date, datetime.datetime)):
|
|
return formats.date_format(value, format=self._get_format(), use_l10n=True)
|
|
|
|
return str(value)
|
|
|
|
def value_from_datadict(self, data, files, name):
|
|
"""Parse the datetime string from the form data."""
|
|
value = super().value_from_datadict(data, files, name)
|
|
if value:
|
|
try:
|
|
# This does it's best to convert Django's PHP-Style date format to a datetime format and reformat the
|
|
# value to be read by Django. Probably could be improved
|
|
return datetime.datetime.strptime(
|
|
value.strip(),
|
|
django_to_python_datetime(self._get_format())
|
|
or django_to_python_datetime(get_format("SHORT_DATETIME_FORMAT")),
|
|
).strftime("%Y-%m-%d %H:%M:%S")
|
|
except (ValueError, TypeError) as e:
|
|
return value
|
|
return None
|
|
|
|
|
|
class AirMonthYearPickerInput(AirDatePickerInput):
|
|
def __init__(self, attrs=None, format=None, *args, **kwargs):
|
|
super().__init__(attrs=attrs, format=format, *args, **kwargs)
|
|
# Store the display format for AirDatepicker
|
|
self.display_format = "MMMM yyyy"
|
|
# Store the Python format for internal use
|
|
self.python_format = "%B %Y"
|
|
|
|
@staticmethod
|
|
def _get_month_names():
|
|
"""Get month names using Django's date translation"""
|
|
return {dates.MONTHS[i]: i for i in range(1, 13)}
|
|
|
|
def build_attrs(self, base_attrs, extra_attrs=None):
|
|
attrs = super().build_attrs(base_attrs, extra_attrs)
|
|
|
|
# Add data attributes for AirDatepicker configuration
|
|
attrs["data-now-button-txt"] = _("Today")
|
|
attrs["data-date-format"] = "MMMM yyyy"
|
|
|
|
return attrs
|
|
|
|
def format_value(self, value):
|
|
"""Format the value for display in the widget."""
|
|
if value:
|
|
self.attrs["data-value"] = (
|
|
value # We use this to dynamically select the initial date on AirDatePicker
|
|
)
|
|
|
|
if value is None:
|
|
return ""
|
|
if isinstance(value, str):
|
|
try:
|
|
value = datetime.datetime.strptime(value, "%Y-%m-%d").date()
|
|
except ValueError:
|
|
return value
|
|
if isinstance(value, (datetime.datetime, datetime.date)):
|
|
# Use Django's date translation
|
|
month_name = dates.MONTHS[value.month]
|
|
return f"{month_name} {value.year}"
|
|
return value
|
|
|
|
def value_from_datadict(self, data, files, name):
|
|
"""Convert the value from the widget format back to a format Django can handle."""
|
|
value = super().value_from_datadict(data, files, name)
|
|
if value:
|
|
try:
|
|
# Split the value into month name and year
|
|
month_str, year_str = value.rsplit(" ", 1)
|
|
year = int(year_str)
|
|
|
|
# Get month number from translated month name
|
|
month_names = self._get_month_names()
|
|
month = month_names.get(month_str)
|
|
|
|
if month and year:
|
|
# Return the first day of the month in Django's expected format
|
|
return datetime.date(year, month, 1).strftime("%Y-%m-%d")
|
|
except (ValueError, KeyError):
|
|
return None
|
|
return None
|
|
|
|
|
|
class AirYearPickerInput(AirDatePickerInput):
|
|
def __init__(self, attrs=None, format=None, *args, **kwargs):
|
|
super().__init__(attrs=attrs, format=format, *args, **kwargs)
|
|
# Store the display format for AirDatepicker
|
|
self.display_format = "yyyy"
|
|
# Store the Python format for internal use
|
|
self.python_format = "%Y"
|
|
|
|
def build_attrs(self, base_attrs, extra_attrs=None):
|
|
attrs = super().build_attrs(base_attrs, extra_attrs)
|
|
|
|
# Add data attributes for AirDatepicker configuration
|
|
attrs["data-now-button-txt"] = _("Today")
|
|
attrs["data-date-format"] = "yyyy"
|
|
|
|
return attrs
|
|
|
|
def format_value(self, value):
|
|
"""Format the value for display in the widget."""
|
|
if value:
|
|
self.attrs["data-value"] = (
|
|
value # We use this to dynamically select the initial date on AirDatePicker
|
|
)
|
|
|
|
if value is None:
|
|
return ""
|
|
if isinstance(value, str):
|
|
try:
|
|
value = datetime.datetime.strptime(value, "%Y-%m-%d").date()
|
|
except ValueError:
|
|
return value
|
|
if isinstance(value, (datetime.datetime, datetime.date)):
|
|
# Use Django's date translation
|
|
return f"{value.year}"
|
|
return value
|
|
|
|
def value_from_datadict(self, data, files, name):
|
|
"""Convert the value from the widget format back to a format Django can handle."""
|
|
value = super().value_from_datadict(data, files, name)
|
|
if value:
|
|
try:
|
|
# Split the value into month name and year
|
|
year_str = value
|
|
year = int(year_str)
|
|
|
|
if year:
|
|
# Return the first day of the month in Django's expected format
|
|
return datetime.date(year, 1, 1).strftime("%Y-%m-%d")
|
|
except (ValueError, KeyError):
|
|
return None
|
|
return None
|