You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

511 lines
14 KiB

from __future__ import unicode_literals
import decimal
import os
from datetime import date, timedelta, datetime, time
from django.conf import settings
from django.core.validators import EMPTY_VALUES
from django.utils.dateparse import (
parse_duration,
parse_datetime,
parse_date,
parse_time,
)
from django.utils.duration import duration_string
from django.utils.encoding import force_str
from datetime import timezone
from django.utils.timezone import (
# utc,
is_aware,
make_aware,
make_naive,
get_default_timezone,
)
from six import string_types, text_type
from django.db.models.fields.files import FieldFile
from rest_framework import serializers
class AboutUsSerializer(serializers.Serializer):
content = serializers.CharField(allow_blank=True)
class AboutUsDobodiSerializer(serializers.Serializer):
arabic_text = serializers.CharField(allow_blank=True)
translated_text = serializers.CharField(allow_blank=True)
title = serializers.CharField(allow_blank=True)
content = serializers.CharField(allow_blank=True)
class FAQItemSerializer(serializers.Serializer):
question = serializers.CharField()
answer = serializers.CharField()
class SupportSerializer(serializers.Serializer):
telegram_number = serializers.CharField()
whatsapp_number = serializers.CharField()
class CardSerializer(serializers.Serializer):
card_number = serializers.CharField()
card_name = serializers.CharField()
whatsapp_number = serializers.CharField()
class UnsetValue(object):
pass
UNSET = UnsetValue()
class SerializationError(Exception):
pass
class BaseSerializer:
"""
A serializer take a Python variable and returns a string that can be stored safely in database
"""
exception = SerializationError
@classmethod
def serialize(cls, value, **kwargs):
"""
Return a string from a Python var
"""
return cls.to_db(value, **kwargs)
@classmethod
def deserialize(cls, value, **kwargs):
"""
Convert a python string to a var
"""
return cls.to_python(value, **kwargs)
@classmethod
def to_python(cls, value, **kwargs):
raise NotImplementedError
@classmethod
def to_db(cls, value, **kwargs):
return text_type(cls.clean_to_db_value(value))
@classmethod
def clean_to_db_value(cls, value):
return value
class InstanciatedSerializer(BaseSerializer):
"""
In some situations, such as with FileSerializer,
we need the serializer to be an instance and not a class
"""
def serialize(self, value, **kwargs):
return self.to_db(value, **kwargs)
def deserialize(self, value, **kwargs):
return self.to_python(value, **kwargs)
def to_python(self, value, **kwargs):
raise NotImplementedError
def to_db(self, value, **kwargs):
return text_type(self.clean_to_db_value(value))
def clean_to_db_value(self, value):
return value
class BooleanSerializer(BaseSerializer):
true = (
"True",
"true",
"TRUE",
"1",
"YES",
"Yes",
"yes",
)
false = (
"False",
"false",
"FALSE",
"0",
"No",
"no",
"NO",
)
@classmethod
def clean_to_db_value(cls, value):
if not isinstance(value, bool):
raise cls.exception("{0} is not a boolean".format(value))
return value
@classmethod
def to_python(cls, value, **kwargs):
if value in cls.true:
return True
elif value in cls.false:
return False
else:
raise cls.exception(
"Value {0} can't be deserialized to a Boolean".format(value)
)
class IntegerSerializer(BaseSerializer):
@classmethod
def clean_to_db_value(cls, value):
if not isinstance(value, int):
raise cls.exception("IntSerializer can only serialize int values")
return value
@classmethod
def to_python(cls, value, **kwargs):
try:
return int(value)
except:
raise cls.exception("Value {0} cannot be converted to int".format(value))
IntSerializer = IntegerSerializer
class DecimalSerializer(BaseSerializer):
@classmethod
def clean_to_db_value(cls, value):
if not isinstance(value, decimal.Decimal):
raise cls.exception(
"DecimalSerializer can only serialize Decimal instances"
)
return value
@classmethod
def to_python(cls, value, **kwargs):
try:
return decimal.Decimal(value)
except decimal.InvalidOperation:
raise cls.exception(
"Value {0} cannot be converted to decimal".format(value)
)
class FloatSerializer(BaseSerializer):
@classmethod
def clean_to_db_value(cls, value):
if not isinstance(value, (int, float)):
raise cls.exception(
"FloatSerializer can only serialize float or int values"
)
return float(value)
@classmethod
def to_python(cls, value, **kwargs):
try:
return float(value)
except float.InvalidOperation:
raise cls.exception("Value {0} cannot be converted to float".format(value))
from django.template import defaultfilters
class StringSerializer(BaseSerializer):
@classmethod
def to_db(cls, value, **kwargs):
if not isinstance(value, string_types):
raise cls.exception(
"Cannot serialize, value {0} is not a string".format(value)
)
if kwargs.get("escape_html", False):
return defaultfilters.force_escape(value)
else:
return value
@classmethod
def to_python(cls, value, **kwargs):
"""String deserialisation just return the value as a string"""
if not value:
return ""
try:
return str(value)
except:
pass
try:
return value.encode("utf-8")
except:
pass
raise cls.exception("Cannot deserialize value {0} tostring".format(value))
class ModelSerializer(InstanciatedSerializer):
model = None
def __init__(self, model):
self.model = model
def to_db(self, value, **kwargs):
if not value or (value == UNSET):
return None
return str(value.pk)
def to_python(self, value, **kwargs):
if value is None:
return
try:
pk = int(value)
return self.model.objects.get(pk=pk)
except:
raise self.exception("Value {0} cannot be converted to pk".format(value))
class ModelMultipleSerializer(ModelSerializer):
separator = ","
sort = True
def to_db(self, value, **kwargs):
if not value:
return
if hasattr(value, "pk"):
# Support single instances in this serializer to allow
# create_deletion_handler to work for model multiple choice preferences
value = [value.pk]
else:
value = list(value.values_list("pk", flat=True))
if self.sort:
value = sorted(value)
return self.separator.join(map(str, value))
def to_python(self, value, **kwargs):
if value in EMPTY_VALUES:
return self.model.objects.none()
try:
pks = value.split(",")
pks = [int(i) if str(i).isdigit() else str(i) for i in pks]
return self.model.objects.filter(pk__in=pks)
except:
raise self.exception("Array {0} cannot be converted to int".format(value))
class PreferenceFieldFile(FieldFile):
"""
In order to have the same API that we have with models.FileField,
we must return a FieldFile object. However, there are various
things we have to override, since our files are not bound to a model
field.
"""
def __init__(self, preference, storage, name):
super(FieldFile, self).__init__(None, name)
# FieldFile also needs a model instance to save changes.
class FakeInstance(object):
"""
FieldFile needs a model instance to update when file is persisted
or deleted
"""
def save(self):
return
self.instance = FakeInstance()
class FakeField(object):
"""
FieldFile needs a field object to generate a filename, persist
and delete files, so we are effectively mocking that.
"""
name = "noop"
attname = "noop"
max_length = 10000
def generate_filename(field, instance, name):
return os.path.join(self.preference.get_upload_path(), f.name)
self.field = FakeField()
self.storage = storage
self._committed = True
self.preference = preference
class FileSerializer(InstanciatedSerializer):
"""
Since this serializer requires additional data from the preference
especially the upload path, we cannot do it without binding it
to the preference
it is therefore designed to be explicitely instanciated by the preference
object.
"""
def __init__(self, preference):
self.preference = preference
def to_db(self, f, **kwargs):
if not f:
return
saved_path = f.name
if not hasattr(f, "save"):
path = os.path.join(self.preference.get_upload_path(), f.name)
saved_path = self.preference.get_file_storage().save(path, f)
return saved_path
def to_python(self, value, **kwargs):
if not value:
return
storage = self.preference.get_file_storage()
return PreferenceFieldFile(
preference=self.preference, storage=storage, name=value
)
class DurationSerializer(BaseSerializer):
@classmethod
def to_db(cls, value, **kwargs):
if not isinstance(value, timedelta):
raise cls.exception(
"Cannot serialize, value {0} is not a timedelta".format(value)
)
return duration_string(value)
@classmethod
def to_python(cls, value, **kwargs):
parsed = parse_duration(force_str(value))
if parsed is None:
raise cls.exception(
"Value {0} cannot be converted to timedelta".format(value)
)
return parsed
class DateSerializer(BaseSerializer):
@classmethod
def to_db(cls, value, **kwargs):
if not isinstance(value, date):
raise cls.exception(
"Cannot serialize, value {0} is not a date object".format(value)
)
return value.isoformat()
@classmethod
def to_python(cls, value, **kwargs):
parsed = parse_date(force_str(value))
if parsed is None:
raise cls.exception(
"Value {0} cannot be converted to a date object".format(value)
)
return parsed
class DateTimeSerializer(BaseSerializer):
@classmethod
def to_db(cls, value, **kwargs):
if not isinstance(value, datetime):
raise cls.exception(
"Cannot serialize, value {0} is not a datetime object".format(value)
)
value = cls.enforce_timezone(value)
return value.isoformat()
@classmethod
def enforce_timezone(cls, value):
"""
When `self.default_timezone` is `None`, always return naive datetimes.
When `self.default_timezone` is not `None`, always return aware datetimes.
"""
field_timezone = cls.default_timezone()
if (field_timezone is not None) and not is_aware(value):
return make_aware(value, field_timezone)
elif (field_timezone is None) and is_aware(value):
return make_naive(value, timezone.utc)
return value
@classmethod
def default_timezone(cls):
return get_default_timezone() if settings.USE_TZ else None
@classmethod
def to_python(cls, value, **kwargs):
parsed = parse_datetime(force_str(value))
if parsed is None:
raise cls.exception(
"Value {0} cannot be converted to a datetime object".format(value)
)
return parsed
class TimeSerializer(BaseSerializer):
@classmethod
def to_db(cls, value, **kwargs):
if not isinstance(value, time):
raise cls.exception(
"Cannot serialize, value {0} is not a time object".format(value)
)
return value.isoformat()
@classmethod
def to_python(cls, value, **kwargs):
parsed = parse_time(force_str(value))
if parsed is None:
raise cls.exception(
"Value {0} cannot be converted to a time object".format(value)
)
return parsed
class MultipleSerializer(BaseSerializer):
separator = ","
sort = True
@classmethod
def to_db(cls, value, **kwargs):
if not value:
return
# This makes the use of the separator in choices safe by duplicating
# it in each value before they are joined later on
# Contract: choices keys cannot be empty
value = [str(v).replace(cls.separator, cls.separator * 2) for v in value]
if "" in value:
raise cls.exception("Choices must not be empty")
if cls.sort:
value = sorted(value)
return cls.separator.join(value)
@classmethod
def to_python(cls, value, **kwargs):
if value in EMPTY_VALUES:
return []
ret = value.split(cls.separator)
# Duplication of separator is reverted (cf. to_db)
while "" in ret:
pos = ret.index("")
val = ret[pos - 1] + cls.separator + ret[pos + 1]
ret = ret[0 : pos - 1] + [val] + ret[pos + 2 :]
return ret