| |
| 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 |