|
|
|
|
|
import datetime |
|
|
from itertools import chain |
|
|
|
|
|
from .parser import Parser |
|
|
from .exceptions import QueryFormatError |
|
|
|
|
|
|
|
|
class Serializer(object): |
|
|
def __init__(self, record, query="{*}", many=False): |
|
|
self.many = many |
|
|
self._record = record |
|
|
self._raw_query = query |
|
|
super().__init__() |
|
|
|
|
|
def get_parsed_restql_query(self): |
|
|
parser = Parser(self._raw_query) |
|
|
try: |
|
|
parsed_restql_query = parser.get_parsed() |
|
|
return parsed_restql_query |
|
|
except SyntaxError as e: |
|
|
msg = "QuerySyntaxError: " + e.msg + " on " + e.text |
|
|
raise SyntaxError(msg) from None |
|
|
except QueryFormatError as e: |
|
|
msg = "QueryFormatError: " + str(e) |
|
|
raise QueryFormatError(msg) from None |
|
|
|
|
|
@property |
|
|
def data(self): |
|
|
parsed_restql_query = self.get_parsed_restql_query() |
|
|
if self.many: |
|
|
return [ |
|
|
self.serialize(rec, parsed_restql_query) |
|
|
for rec |
|
|
in self._record |
|
|
] |
|
|
return self.serialize(self._record, parsed_restql_query) |
|
|
|
|
|
@classmethod |
|
|
def build_flat_field(cls, rec, field_name): |
|
|
all_fields = rec.fields_get() |
|
|
if field_name not in all_fields: |
|
|
msg = "'%s' field is not found" % field_name |
|
|
raise LookupError(msg) |
|
|
field_type = rec.fields_get(field_name).get(field_name).get('type') |
|
|
if field_type in ['one2many', 'many2many']: |
|
|
return { |
|
|
field_name: [record.id for record in rec[field_name]] |
|
|
} |
|
|
elif field_type in ['many2one']: |
|
|
return {field_name: rec[field_name].id} |
|
|
elif field_type == 'datetime' and rec[field_name]: |
|
|
return { |
|
|
field_name: rec[field_name].strftime("%Y-%m-%d-%H-%M") |
|
|
} |
|
|
elif field_type == 'date' and rec[field_name]: |
|
|
return { |
|
|
field_name: rec[field_name].strftime("%Y-%m-%d") |
|
|
} |
|
|
elif field_type == 'time' and rec[field_name]: |
|
|
return { |
|
|
field_name: rec[field_name].strftime("%H-%M-%S") |
|
|
} |
|
|
elif field_type == "binary" and isinstance(rec[field_name], bytes) and rec[field_name]: |
|
|
return {field_name: rec[field_name].decode("utf-8")} |
|
|
else: |
|
|
return {field_name: rec[field_name]} |
|
|
|
|
|
@classmethod |
|
|
def build_nested_field(cls, rec, field_name, nested_parsed_query): |
|
|
all_fields = rec.fields_get() |
|
|
if field_name not in all_fields: |
|
|
msg = "'%s' field is not found" % field_name |
|
|
raise LookupError(msg) |
|
|
field_type = rec.fields_get(field_name).get(field_name).get('type') |
|
|
if field_type in ['one2many', 'many2many']: |
|
|
return { |
|
|
field_name: [ |
|
|
cls.serialize(record, nested_parsed_query) |
|
|
for record |
|
|
in rec[field_name] |
|
|
] |
|
|
} |
|
|
elif field_type in ['many2one']: |
|
|
return { |
|
|
field_name: cls.serialize(rec[field_name], nested_parsed_query) |
|
|
} |
|
|
else: |
|
|
|
|
|
msg = "'%s' is not a nested field" % field_name |
|
|
raise ValueError(msg) |
|
|
|
|
|
@classmethod |
|
|
def serialize(cls, rec, parsed_query): |
|
|
data = {} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if parsed_query["exclude"]: |
|
|
|
|
|
all_fields = rec.fields_get() |
|
|
for field in parsed_query["include"]: |
|
|
if field == "*": |
|
|
continue |
|
|
for nested_field, nested_parsed_query in field.items(): |
|
|
built_nested_field = cls.build_nested_field( |
|
|
rec, |
|
|
nested_field, |
|
|
nested_parsed_query |
|
|
) |
|
|
data.update(built_nested_field) |
|
|
|
|
|
flat_fields= set(all_fields).symmetric_difference(set(parsed_query['exclude'])) |
|
|
for field in flat_fields: |
|
|
flat_field = cls.build_flat_field(rec, field) |
|
|
data.update(flat_field) |
|
|
|
|
|
elif parsed_query["include"]: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
all_fields = rec.fields_get() |
|
|
if "*" in parsed_query['include']: |
|
|
|
|
|
parsed_query['include'] = filter( |
|
|
lambda item: item != "*", |
|
|
parsed_query['include'] |
|
|
) |
|
|
fields = chain(parsed_query['include'], all_fields) |
|
|
parsed_query['include'] = list(fields) |
|
|
|
|
|
for field in parsed_query["include"]: |
|
|
if isinstance(field, dict): |
|
|
for nested_field, nested_parsed_query in field.items(): |
|
|
built_nested_field = cls.build_nested_field( |
|
|
rec, |
|
|
nested_field, |
|
|
nested_parsed_query |
|
|
) |
|
|
data.update(built_nested_field) |
|
|
else: |
|
|
flat_field = cls.build_flat_field(rec, field) |
|
|
data.update(flat_field) |
|
|
else: |
|
|
|
|
|
|
|
|
return {} |
|
|
return data |