Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / quotaholder_app / callpoint.py @ 548938f6

History | View | Annotate | Download (10.3 kB)

1 1d734153 Giorgos Korfiatis
# Copyright 2012, 2013 GRNET S.A. All rights reserved.
2 3e5941c1 Giorgos Korfiatis
#
3 3e5941c1 Giorgos Korfiatis
# Redistribution and use in source and binary forms, with or
4 3e5941c1 Giorgos Korfiatis
# without modification, are permitted provided that the following
5 3e5941c1 Giorgos Korfiatis
# conditions are met:
6 3e5941c1 Giorgos Korfiatis
#
7 3e5941c1 Giorgos Korfiatis
#   1. Redistributions of source code must retain the above
8 3e5941c1 Giorgos Korfiatis
#      copyright notice, this list of conditions and the following
9 3e5941c1 Giorgos Korfiatis
#      disclaimer.
10 3e5941c1 Giorgos Korfiatis
#
11 3e5941c1 Giorgos Korfiatis
#   2. Redistributions in binary form must reproduce the above
12 3e5941c1 Giorgos Korfiatis
#      copyright notice, this list of conditions and the following
13 3e5941c1 Giorgos Korfiatis
#      disclaimer in the documentation and/or other materials
14 3e5941c1 Giorgos Korfiatis
#      provided with the distribution.
15 3e5941c1 Giorgos Korfiatis
#
16 3e5941c1 Giorgos Korfiatis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 3e5941c1 Giorgos Korfiatis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 3e5941c1 Giorgos Korfiatis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 3e5941c1 Giorgos Korfiatis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 3e5941c1 Giorgos Korfiatis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 3e5941c1 Giorgos Korfiatis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 3e5941c1 Giorgos Korfiatis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 3e5941c1 Giorgos Korfiatis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 3e5941c1 Giorgos Korfiatis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 3e5941c1 Giorgos Korfiatis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 3e5941c1 Giorgos Korfiatis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 3e5941c1 Giorgos Korfiatis
# POSSIBILITY OF SUCH DAMAGE.
28 3e5941c1 Giorgos Korfiatis
#
29 3e5941c1 Giorgos Korfiatis
# The views and conclusions contained in the software and
30 3e5941c1 Giorgos Korfiatis
# documentation are those of the authors and should not be
31 3e5941c1 Giorgos Korfiatis
# interpreted as representing official policies, either expressed
32 3e5941c1 Giorgos Korfiatis
# or implied, of GRNET S.A.
33 3e5941c1 Giorgos Korfiatis
34 164e64d5 Giorgos Korfiatis
from datetime import datetime
35 5a0f9d6c Giorgos Korfiatis
from django.db.models import Q
36 ec5e00aa Giorgos Korfiatis
from astakos.quotaholder_app.exception import (
37 b0727daf Giorgos Korfiatis
    QuotaholderError,
38 79e3da8a Giorgos Korfiatis
    NoCommissionError,
39 b0727daf Giorgos Korfiatis
    CorruptedError, InvalidDataError,
40 79e3da8a Giorgos Korfiatis
    NoHoldingError,
41 1d734153 Giorgos Korfiatis
    DuplicateError)
42 fac1de87 Georgios D. Tsoukalas
43 ec5e00aa Giorgos Korfiatis
from astakos.quotaholder_app.commission import (
44 6c0f4562 Giorgos Korfiatis
    Import, Release, Operations, finalize, undo)
45 d03796c2 Giorgos Korfiatis
46 ea1369dc Giorgos Korfiatis
from astakos.quotaholder_app.models import (
47 164e64d5 Giorgos Korfiatis
    Holding, Commission, Provision, ProvisionLog)
48 fac1de87 Georgios D. Tsoukalas
49 e5a2e942 Georgios D. Tsoukalas
50 c3b42b86 Giorgos Korfiatis
def format_datetime(d):
51 c3b42b86 Giorgos Korfiatis
    return d.strftime('%Y-%m-%dT%H:%M:%S.%f')[:24]
52 c3b42b86 Giorgos Korfiatis
53 c3b42b86 Giorgos Korfiatis
54 5a0f9d6c Giorgos Korfiatis
def get_quota(holders=None, sources=None, resources=None, flt=None):
55 5a0f9d6c Giorgos Korfiatis
    if flt is None:
56 5a0f9d6c Giorgos Korfiatis
        flt = Q()
57 5a0f9d6c Giorgos Korfiatis
58 5a0f9d6c Giorgos Korfiatis
    holdings = Holding.objects.filter(flt)
59 fac1de87 Georgios D. Tsoukalas
60 948e15bc Giorgos Korfiatis
    if holders is not None:
61 948e15bc Giorgos Korfiatis
        holdings = holdings.filter(holder__in=holders)
62 5afce44d Giorgos Korfiatis
63 948e15bc Giorgos Korfiatis
    if sources is not None:
64 948e15bc Giorgos Korfiatis
        holdings = holdings.filter(source__in=sources)
65 dc9da5b9 Giorgos Korfiatis
66 948e15bc Giorgos Korfiatis
    if resources is not None:
67 948e15bc Giorgos Korfiatis
        holdings = holdings.filter(resource__in=resources)
68 dc9da5b9 Giorgos Korfiatis
69 948e15bc Giorgos Korfiatis
    quotas = {}
70 948e15bc Giorgos Korfiatis
    for holding in holdings:
71 948e15bc Giorgos Korfiatis
        key = (holding.holder, holding.source, holding.resource)
72 68b991bc Giorgos Korfiatis
        value = (holding.limit, holding.usage_min, holding.usage_max)
73 948e15bc Giorgos Korfiatis
        quotas[key] = value
74 dc9da5b9 Giorgos Korfiatis
75 948e15bc Giorgos Korfiatis
    return quotas
76 dc9da5b9 Giorgos Korfiatis
77 dc9da5b9 Giorgos Korfiatis
78 548938f6 Giorgos Korfiatis
def _get_holdings_for_update(holding_keys, resource=None, delete=False):
79 548938f6 Giorgos Korfiatis
    flt = Q(resource=resource) if resource is not None else Q()
80 f7b7da5d Giorgos Korfiatis
    holders = set(holder for (holder, source, resource) in holding_keys)
81 548938f6 Giorgos Korfiatis
    objs = Holding.objects.filter(flt, holder__in=holders).order_by('pk')
82 20c6de35 Giorgos Korfiatis
    hs = objs.select_for_update()
83 f7b7da5d Giorgos Korfiatis
84 20c6de35 Giorgos Korfiatis
    keys = set(holding_keys)
85 948e15bc Giorgos Korfiatis
    holdings = {}
86 20c6de35 Giorgos Korfiatis
    put_back = []
87 f7b7da5d Giorgos Korfiatis
    for h in hs:
88 f7b7da5d Giorgos Korfiatis
        key = h.holder, h.source, h.resource
89 20c6de35 Giorgos Korfiatis
        if key in keys:
90 20c6de35 Giorgos Korfiatis
            holdings[key] = h
91 20c6de35 Giorgos Korfiatis
        else:
92 20c6de35 Giorgos Korfiatis
            put_back.append(h)
93 20c6de35 Giorgos Korfiatis
94 20c6de35 Giorgos Korfiatis
    if delete:
95 20c6de35 Giorgos Korfiatis
        objs.delete()
96 20c6de35 Giorgos Korfiatis
        Holding.objects.bulk_create(put_back)
97 948e15bc Giorgos Korfiatis
    return holdings
98 ae16bcad Giorgos Korfiatis
99 ae16bcad Giorgos Korfiatis
100 948e15bc Giorgos Korfiatis
def _mkProvision(key, quantity):
101 948e15bc Giorgos Korfiatis
    holder, source, resource = key
102 948e15bc Giorgos Korfiatis
    return {'holder': holder,
103 948e15bc Giorgos Korfiatis
            'source': source,
104 948e15bc Giorgos Korfiatis
            'resource': resource,
105 948e15bc Giorgos Korfiatis
            'quantity': quantity,
106 948e15bc Giorgos Korfiatis
            }
107 948e15bc Giorgos Korfiatis
108 948e15bc Giorgos Korfiatis
109 548938f6 Giorgos Korfiatis
def set_quota(quotas, resource=None):
110 948e15bc Giorgos Korfiatis
    holding_keys = [key for (key, limit) in quotas]
111 548938f6 Giorgos Korfiatis
    holdings = _get_holdings_for_update(
112 548938f6 Giorgos Korfiatis
        holding_keys, resource=resource, delete=True)
113 948e15bc Giorgos Korfiatis
114 20c6de35 Giorgos Korfiatis
    new_holdings = {}
115 948e15bc Giorgos Korfiatis
    for key, limit in quotas:
116 548938f6 Giorgos Korfiatis
        holder, source, res = key
117 548938f6 Giorgos Korfiatis
        if resource is not None and resource != res:
118 548938f6 Giorgos Korfiatis
            continue
119 20c6de35 Giorgos Korfiatis
        h = Holding(holder=holder,
120 20c6de35 Giorgos Korfiatis
                    source=source,
121 548938f6 Giorgos Korfiatis
                    resource=res,
122 20c6de35 Giorgos Korfiatis
                    limit=limit)
123 948e15bc Giorgos Korfiatis
        try:
124 20c6de35 Giorgos Korfiatis
            h_old = holdings[key]
125 20c6de35 Giorgos Korfiatis
            h.usage_min = h_old.usage_min
126 20c6de35 Giorgos Korfiatis
            h.usage_max = h_old.usage_max
127 948e15bc Giorgos Korfiatis
        except KeyError:
128 20c6de35 Giorgos Korfiatis
            pass
129 20c6de35 Giorgos Korfiatis
        new_holdings[key] = h
130 20c6de35 Giorgos Korfiatis
131 20c6de35 Giorgos Korfiatis
    Holding.objects.bulk_create(new_holdings.values())
132 ae16bcad Giorgos Korfiatis
133 9747707e Giorgos Korfiatis
134 643b3d82 Giorgos Korfiatis
def issue_commission(clientkey, provisions, name="", force=False):
135 948e15bc Giorgos Korfiatis
    operations = Operations()
136 948e15bc Giorgos Korfiatis
    provisions_to_create = []
137 79e3da8a Giorgos Korfiatis
138 948e15bc Giorgos Korfiatis
    keys = [key for (key, value) in provisions]
139 948e15bc Giorgos Korfiatis
    holdings = _get_holdings_for_update(keys)
140 948e15bc Giorgos Korfiatis
    try:
141 948e15bc Giorgos Korfiatis
        checked = []
142 948e15bc Giorgos Korfiatis
        for key, quantity in provisions:
143 948e15bc Giorgos Korfiatis
            if not isinstance(quantity, (int, long)):
144 948e15bc Giorgos Korfiatis
                raise InvalidDataError("Malformed provision")
145 79e3da8a Giorgos Korfiatis
146 948e15bc Giorgos Korfiatis
            if key in checked:
147 948e15bc Giorgos Korfiatis
                m = "Duplicate provision for %s" % str(key)
148 948e15bc Giorgos Korfiatis
                provision = _mkProvision(key, quantity)
149 948e15bc Giorgos Korfiatis
                raise DuplicateError(m,
150 948e15bc Giorgos Korfiatis
                                     provision=provision)
151 948e15bc Giorgos Korfiatis
            checked.append(key)
152 e5a2e942 Georgios D. Tsoukalas
153 948e15bc Giorgos Korfiatis
            # Target
154 948e15bc Giorgos Korfiatis
            try:
155 948e15bc Giorgos Korfiatis
                th = holdings[key]
156 948e15bc Giorgos Korfiatis
            except KeyError:
157 948e15bc Giorgos Korfiatis
                m = ("There is no such holding %s" % str(key))
158 948e15bc Giorgos Korfiatis
                provision = _mkProvision(key, quantity)
159 948e15bc Giorgos Korfiatis
                raise NoHoldingError(m,
160 948e15bc Giorgos Korfiatis
                                     provision=provision)
161 948e15bc Giorgos Korfiatis
162 948e15bc Giorgos Korfiatis
            if quantity >= 0:
163 948e15bc Giorgos Korfiatis
                operations.prepare(Import, th, quantity, force)
164 948e15bc Giorgos Korfiatis
165 948e15bc Giorgos Korfiatis
            else:  # release
166 948e15bc Giorgos Korfiatis
                abs_quantity = -quantity
167 01b8fb9a Giorgos Korfiatis
                operations.prepare(Release, th, abs_quantity, False)
168 948e15bc Giorgos Korfiatis
169 948e15bc Giorgos Korfiatis
            holdings[key] = th
170 948e15bc Giorgos Korfiatis
            provisions_to_create.append((key, quantity))
171 948e15bc Giorgos Korfiatis
172 948e15bc Giorgos Korfiatis
    except QuotaholderError:
173 948e15bc Giorgos Korfiatis
        operations.revert()
174 948e15bc Giorgos Korfiatis
        raise
175 948e15bc Giorgos Korfiatis
176 3679f852 Giorgos Korfiatis
    commission = Commission.objects.create(clientkey=clientkey,
177 3679f852 Giorgos Korfiatis
                                           name=name,
178 164e64d5 Giorgos Korfiatis
                                           issue_datetime=datetime.now())
179 948e15bc Giorgos Korfiatis
    for (holder, source, resource), quantity in provisions_to_create:
180 948e15bc Giorgos Korfiatis
        Provision.objects.create(serial=commission,
181 948e15bc Giorgos Korfiatis
                                 holder=holder,
182 948e15bc Giorgos Korfiatis
                                 source=source,
183 948e15bc Giorgos Korfiatis
                                 resource=resource,
184 948e15bc Giorgos Korfiatis
                                 quantity=quantity)
185 948e15bc Giorgos Korfiatis
186 948e15bc Giorgos Korfiatis
    return commission.serial
187 948e15bc Giorgos Korfiatis
188 948e15bc Giorgos Korfiatis
189 164e64d5 Giorgos Korfiatis
def _log_provision(commission, provision, holding, log_datetime, reason):
190 948e15bc Giorgos Korfiatis
191 948e15bc Giorgos Korfiatis
    kwargs = {
192 948e15bc Giorgos Korfiatis
        'serial':              commission.serial,
193 948e15bc Giorgos Korfiatis
        'name':                commission.name,
194 948e15bc Giorgos Korfiatis
        'holder':              holding.holder,
195 948e15bc Giorgos Korfiatis
        'source':              holding.source,
196 948e15bc Giorgos Korfiatis
        'resource':            holding.resource,
197 948e15bc Giorgos Korfiatis
        'limit':               holding.limit,
198 68b991bc Giorgos Korfiatis
        'usage_min':           holding.usage_min,
199 68b991bc Giorgos Korfiatis
        'usage_max':           holding.usage_max,
200 948e15bc Giorgos Korfiatis
        'delta_quantity':      provision.quantity,
201 c3b42b86 Giorgos Korfiatis
        'issue_time':          format_datetime(commission.issue_datetime),
202 c3b42b86 Giorgos Korfiatis
        'log_time':            format_datetime(log_datetime),
203 948e15bc Giorgos Korfiatis
        'reason':              reason,
204 948e15bc Giorgos Korfiatis
    }
205 948e15bc Giorgos Korfiatis
206 948e15bc Giorgos Korfiatis
    ProvisionLog.objects.create(**kwargs)
207 948e15bc Giorgos Korfiatis
208 948e15bc Giorgos Korfiatis
209 948e15bc Giorgos Korfiatis
def _get_commissions_for_update(clientkey, serials):
210 948e15bc Giorgos Korfiatis
    cs = Commission.objects.filter(
211 948e15bc Giorgos Korfiatis
        clientkey=clientkey, serial__in=serials).select_for_update()
212 948e15bc Giorgos Korfiatis
213 948e15bc Giorgos Korfiatis
    commissions = {}
214 948e15bc Giorgos Korfiatis
    for c in cs:
215 948e15bc Giorgos Korfiatis
        commissions[c.serial] = c
216 948e15bc Giorgos Korfiatis
    return commissions
217 948e15bc Giorgos Korfiatis
218 948e15bc Giorgos Korfiatis
219 948e15bc Giorgos Korfiatis
def _partition_by(f, l):
220 948e15bc Giorgos Korfiatis
    d = {}
221 948e15bc Giorgos Korfiatis
    for x in l:
222 948e15bc Giorgos Korfiatis
        group = f(x)
223 948e15bc Giorgos Korfiatis
        group_l = d.get(group, [])
224 948e15bc Giorgos Korfiatis
        group_l.append(x)
225 948e15bc Giorgos Korfiatis
        d[group] = group_l
226 948e15bc Giorgos Korfiatis
    return d
227 948e15bc Giorgos Korfiatis
228 948e15bc Giorgos Korfiatis
229 643b3d82 Giorgos Korfiatis
def resolve_pending_commissions(clientkey, accept_set=None, reject_set=None,
230 948e15bc Giorgos Korfiatis
                                reason=''):
231 643b3d82 Giorgos Korfiatis
    if accept_set is None:
232 643b3d82 Giorgos Korfiatis
        accept_set = []
233 643b3d82 Giorgos Korfiatis
    if reject_set is None:
234 643b3d82 Giorgos Korfiatis
        reject_set = []
235 643b3d82 Giorgos Korfiatis
236 948e15bc Giorgos Korfiatis
    actions = dict.fromkeys(accept_set, True)
237 948e15bc Giorgos Korfiatis
    conflicting = set()
238 948e15bc Giorgos Korfiatis
    for serial in reject_set:
239 948e15bc Giorgos Korfiatis
        if actions.get(serial) is True:
240 948e15bc Giorgos Korfiatis
            actions.pop(serial)
241 948e15bc Giorgos Korfiatis
            conflicting.add(serial)
242 948e15bc Giorgos Korfiatis
        else:
243 948e15bc Giorgos Korfiatis
            actions[serial] = False
244 948e15bc Giorgos Korfiatis
245 948e15bc Giorgos Korfiatis
    conflicting = list(conflicting)
246 948e15bc Giorgos Korfiatis
    serials = actions.keys()
247 948e15bc Giorgos Korfiatis
    commissions = _get_commissions_for_update(clientkey, serials)
248 948e15bc Giorgos Korfiatis
    ps = Provision.objects.filter(serial__in=serials).select_for_update()
249 948e15bc Giorgos Korfiatis
    holding_keys = sorted(p.holding_key() for p in ps)
250 948e15bc Giorgos Korfiatis
    holdings = _get_holdings_for_update(holding_keys)
251 948e15bc Giorgos Korfiatis
    provisions = _partition_by(lambda p: p.serial_id, ps)
252 948e15bc Giorgos Korfiatis
253 164e64d5 Giorgos Korfiatis
    log_datetime = datetime.now()
254 948e15bc Giorgos Korfiatis
255 948e15bc Giorgos Korfiatis
    accepted, rejected, notFound = [], [], []
256 948e15bc Giorgos Korfiatis
    for serial, accept in actions.iteritems():
257 948e15bc Giorgos Korfiatis
        commission = commissions.get(serial)
258 948e15bc Giorgos Korfiatis
        if commission is None:
259 948e15bc Giorgos Korfiatis
            notFound.append(serial)
260 948e15bc Giorgos Korfiatis
            continue
261 948e15bc Giorgos Korfiatis
262 948e15bc Giorgos Korfiatis
        accepted.append(serial) if accept else rejected.append(serial)
263 948e15bc Giorgos Korfiatis
264 7585a768 Giorgos Korfiatis
        ps = provisions.get(serial, [])
265 948e15bc Giorgos Korfiatis
        for pv in ps:
266 948e15bc Giorgos Korfiatis
            key = pv.holding_key()
267 948e15bc Giorgos Korfiatis
            h = holdings.get(key)
268 948e15bc Giorgos Korfiatis
            if h is None:
269 948e15bc Giorgos Korfiatis
                raise CorruptedError("Corrupted provision")
270 948e15bc Giorgos Korfiatis
271 948e15bc Giorgos Korfiatis
            quantity = pv.quantity
272 948e15bc Giorgos Korfiatis
            action = finalize if accept else undo
273 948e15bc Giorgos Korfiatis
            if quantity >= 0:
274 948e15bc Giorgos Korfiatis
                action(Import, h, quantity)
275 948e15bc Giorgos Korfiatis
            else:  # release
276 948e15bc Giorgos Korfiatis
                action(Release, h, -quantity)
277 948e15bc Giorgos Korfiatis
278 948e15bc Giorgos Korfiatis
            prefix = 'ACCEPT:' if accept else 'REJECT:'
279 948e15bc Giorgos Korfiatis
            comm_reason = prefix + reason[-121:]
280 164e64d5 Giorgos Korfiatis
            _log_provision(commission, pv, h, log_datetime, comm_reason)
281 948e15bc Giorgos Korfiatis
            pv.delete()
282 948e15bc Giorgos Korfiatis
        commission.delete()
283 948e15bc Giorgos Korfiatis
    return accepted, rejected, notFound, conflicting
284 948e15bc Giorgos Korfiatis
285 948e15bc Giorgos Korfiatis
286 948e15bc Giorgos Korfiatis
def resolve_pending_commission(clientkey, serial, accept=True):
287 948e15bc Giorgos Korfiatis
    if accept:
288 948e15bc Giorgos Korfiatis
        ok, notOk, notF, confl = resolve_pending_commissions(
289 948e15bc Giorgos Korfiatis
            clientkey=clientkey, accept_set=[serial])
290 948e15bc Giorgos Korfiatis
    else:
291 948e15bc Giorgos Korfiatis
        notOk, ok, notF, confl = resolve_pending_commissions(
292 948e15bc Giorgos Korfiatis
            clientkey=clientkey, reject_set=[serial])
293 948e15bc Giorgos Korfiatis
294 948e15bc Giorgos Korfiatis
    assert notOk == confl == []
295 948e15bc Giorgos Korfiatis
    assert ok + notF == [serial]
296 948e15bc Giorgos Korfiatis
    return bool(ok)
297 948e15bc Giorgos Korfiatis
298 948e15bc Giorgos Korfiatis
299 643b3d82 Giorgos Korfiatis
def get_pending_commissions(clientkey):
300 948e15bc Giorgos Korfiatis
    pending = Commission.objects.filter(clientkey=clientkey)
301 948e15bc Giorgos Korfiatis
    pending_list = pending.values_list('serial', flat=True)
302 948e15bc Giorgos Korfiatis
    return list(pending_list)
303 948e15bc Giorgos Korfiatis
304 948e15bc Giorgos Korfiatis
305 643b3d82 Giorgos Korfiatis
def get_commission(clientkey, serial):
306 948e15bc Giorgos Korfiatis
    try:
307 948e15bc Giorgos Korfiatis
        commission = Commission.objects.get(clientkey=clientkey,
308 948e15bc Giorgos Korfiatis
                                            serial=serial)
309 948e15bc Giorgos Korfiatis
    except Commission.DoesNotExist:
310 948e15bc Giorgos Korfiatis
        raise NoCommissionError(serial)
311 948e15bc Giorgos Korfiatis
312 a4451f59 Giorgos Korfiatis
    objs = Provision.objects
313 948e15bc Giorgos Korfiatis
    provisions = objs.filter(serial=commission)
314 948e15bc Giorgos Korfiatis
315 948e15bc Giorgos Korfiatis
    ps = [p.todict() for p in provisions]
316 948e15bc Giorgos Korfiatis
317 948e15bc Giorgos Korfiatis
    response = {'serial':     serial,
318 948e15bc Giorgos Korfiatis
                'provisions': ps,
319 164e64d5 Giorgos Korfiatis
                'issue_time': commission.issue_datetime,
320 3a1bed03 Giorgos Korfiatis
                'name':       commission.name,
321 948e15bc Giorgos Korfiatis
                }
322 948e15bc Giorgos Korfiatis
    return response