File size: 5,843 Bytes
f9dacfc |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# -*- coding: utf-8 -*-
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:
# Not a neste field
msg = "'%s' is not a nested field" % field_name
raise ValueError(msg)
@classmethod
def serialize(cls, rec, parsed_query):
data = {}
# NOTE: self.parsed_restql_query["include"] not being empty
# is not a guarantee that the exclude operator(-) has not been
# used because the same self.parsed_restql_query["include"]
# is used to store nested fields when the exclude operator(-) is used
if parsed_query["exclude"]:
# Exclude fields from a query
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"]:
# Here we are sure that self.parsed_restql_query["exclude"]
# is empty which means the exclude operator(-) is not used,
# so self.parsed_restql_query["include"] contains only fields
# to include
all_fields = rec.fields_get()
if "*" in parsed_query['include']:
# Include all fields
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:
# The query is empty i.e query={}
# return nothing
return {}
return data |