Statistics
| Branch: | Tag: | Revision:

root / snf-astakos-app / astakos / quotaholder / callpoint.py @ 68b991bc

History | View | Annotate | Download (10 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 9747707e Giorgos Korfiatis
from django.db.models import F
35 b0727daf Giorgos Korfiatis
from astakos.quotaholder.exception import (
36 b0727daf Giorgos Korfiatis
    QuotaholderError,
37 79e3da8a Giorgos Korfiatis
    NoCommissionError,
38 b0727daf Giorgos Korfiatis
    CorruptedError, InvalidDataError,
39 79e3da8a Giorgos Korfiatis
    NoHoldingError,
40 1d734153 Giorgos Korfiatis
    DuplicateError)
41 fac1de87 Georgios D. Tsoukalas
42 d03796c2 Giorgos Korfiatis
from astakos.quotaholder.commission import (
43 6c0f4562 Giorgos Korfiatis
    Import, Release, Operations, finalize, undo)
44 d03796c2 Giorgos Korfiatis
45 2b888e60 Giorgos Korfiatis
from .models import (Holding,
46 8cff5e41 Giorgos Korfiatis
                     Commission, Provision, ProvisionLog,
47 6c0f4562 Giorgos Korfiatis
                     now)
48 fac1de87 Georgios D. Tsoukalas
49 e5a2e942 Georgios D. Tsoukalas
50 948e15bc Giorgos Korfiatis
def get_quota(holders=None, sources=None, resources=None):
51 948e15bc Giorgos Korfiatis
    holdings = Holding.objects.all()
52 fac1de87 Georgios D. Tsoukalas
53 948e15bc Giorgos Korfiatis
    if holders is not None:
54 948e15bc Giorgos Korfiatis
        holdings = holdings.filter(holder__in=holders)
55 5afce44d Giorgos Korfiatis
56 948e15bc Giorgos Korfiatis
    if sources is not None:
57 948e15bc Giorgos Korfiatis
        holdings = holdings.filter(source__in=sources)
58 dc9da5b9 Giorgos Korfiatis
59 948e15bc Giorgos Korfiatis
    if resources is not None:
60 948e15bc Giorgos Korfiatis
        holdings = holdings.filter(resource__in=resources)
61 dc9da5b9 Giorgos Korfiatis
62 948e15bc Giorgos Korfiatis
    quotas = {}
63 948e15bc Giorgos Korfiatis
    for holding in holdings:
64 948e15bc Giorgos Korfiatis
        key = (holding.holder, holding.source, holding.resource)
65 68b991bc Giorgos Korfiatis
        value = (holding.limit, holding.usage_min, holding.usage_max)
66 948e15bc Giorgos Korfiatis
        quotas[key] = value
67 dc9da5b9 Giorgos Korfiatis
68 948e15bc Giorgos Korfiatis
    return quotas
69 dc9da5b9 Giorgos Korfiatis
70 dc9da5b9 Giorgos Korfiatis
71 948e15bc Giorgos Korfiatis
def _get_holdings_for_update(holding_keys):
72 948e15bc Giorgos Korfiatis
    holding_keys = sorted(holding_keys)
73 948e15bc Giorgos Korfiatis
    holdings = {}
74 948e15bc Giorgos Korfiatis
    for (holder, source, resource) in holding_keys:
75 948e15bc Giorgos Korfiatis
        try:
76 948e15bc Giorgos Korfiatis
            h = Holding.objects.get_for_update(
77 948e15bc Giorgos Korfiatis
                holder=holder, source=source, resource=resource)
78 948e15bc Giorgos Korfiatis
            holdings[(holder, source, resource)] = h
79 948e15bc Giorgos Korfiatis
        except Holding.DoesNotExist:
80 948e15bc Giorgos Korfiatis
            pass
81 948e15bc Giorgos Korfiatis
    return holdings
82 ae16bcad Giorgos Korfiatis
83 ae16bcad Giorgos Korfiatis
84 948e15bc Giorgos Korfiatis
def _mkProvision(key, quantity):
85 948e15bc Giorgos Korfiatis
    holder, source, resource = key
86 948e15bc Giorgos Korfiatis
    return {'holder': holder,
87 948e15bc Giorgos Korfiatis
            'source': source,
88 948e15bc Giorgos Korfiatis
            'resource': resource,
89 948e15bc Giorgos Korfiatis
            'quantity': quantity,
90 948e15bc Giorgos Korfiatis
            }
91 948e15bc Giorgos Korfiatis
92 948e15bc Giorgos Korfiatis
93 948e15bc Giorgos Korfiatis
def set_quota(quotas):
94 948e15bc Giorgos Korfiatis
    holding_keys = [key for (key, limit) in quotas]
95 948e15bc Giorgos Korfiatis
    holdings = _get_holdings_for_update(holding_keys)
96 948e15bc Giorgos Korfiatis
97 948e15bc Giorgos Korfiatis
    for key, limit in quotas:
98 948e15bc Giorgos Korfiatis
        try:
99 948e15bc Giorgos Korfiatis
            h = holdings[key]
100 948e15bc Giorgos Korfiatis
        except KeyError:
101 948e15bc Giorgos Korfiatis
            holder, source, resource = key
102 948e15bc Giorgos Korfiatis
            h = Holding(holder=holder,
103 948e15bc Giorgos Korfiatis
                        source=source,
104 948e15bc Giorgos Korfiatis
                        resource=resource)
105 948e15bc Giorgos Korfiatis
        h.limit = limit
106 948e15bc Giorgos Korfiatis
        h.save()
107 948e15bc Giorgos Korfiatis
        holdings[key] = h
108 ae16bcad Giorgos Korfiatis
109 9747707e Giorgos Korfiatis
110 948e15bc Giorgos Korfiatis
def add_resource_limit(holders=None, sources=None, resources=None, diff=0):
111 948e15bc Giorgos Korfiatis
    holdings = Holding.objects.all()
112 fac1de87 Georgios D. Tsoukalas
113 948e15bc Giorgos Korfiatis
    if holders is not None:
114 948e15bc Giorgos Korfiatis
        holdings = holdings.filter(holder__in=holders)
115 fac1de87 Georgios D. Tsoukalas
116 948e15bc Giorgos Korfiatis
    if sources is not None:
117 948e15bc Giorgos Korfiatis
        holdings = holdings.filter(source__in=sources)
118 6afede3f Giorgos Korfiatis
119 948e15bc Giorgos Korfiatis
    if resources is not None:
120 948e15bc Giorgos Korfiatis
        holdings = holdings.filter(resource__in=resources)
121 6c0f4562 Giorgos Korfiatis
122 948e15bc Giorgos Korfiatis
    holdings.update(limit=F('limit')+diff)
123 fac1de87 Georgios D. Tsoukalas
124 79e3da8a Giorgos Korfiatis
125 643b3d82 Giorgos Korfiatis
def issue_commission(clientkey, provisions, name="", force=False):
126 948e15bc Giorgos Korfiatis
    operations = Operations()
127 948e15bc Giorgos Korfiatis
    provisions_to_create = []
128 79e3da8a Giorgos Korfiatis
129 948e15bc Giorgos Korfiatis
    keys = [key for (key, value) in provisions]
130 948e15bc Giorgos Korfiatis
    holdings = _get_holdings_for_update(keys)
131 948e15bc Giorgos Korfiatis
    try:
132 948e15bc Giorgos Korfiatis
        checked = []
133 948e15bc Giorgos Korfiatis
        for key, quantity in provisions:
134 948e15bc Giorgos Korfiatis
            if not isinstance(quantity, (int, long)):
135 948e15bc Giorgos Korfiatis
                raise InvalidDataError("Malformed provision")
136 79e3da8a Giorgos Korfiatis
137 948e15bc Giorgos Korfiatis
            if key in checked:
138 948e15bc Giorgos Korfiatis
                m = "Duplicate provision for %s" % str(key)
139 948e15bc Giorgos Korfiatis
                provision = _mkProvision(key, quantity)
140 948e15bc Giorgos Korfiatis
                raise DuplicateError(m,
141 948e15bc Giorgos Korfiatis
                                     provision=provision)
142 948e15bc Giorgos Korfiatis
            checked.append(key)
143 e5a2e942 Georgios D. Tsoukalas
144 948e15bc Giorgos Korfiatis
            # Target
145 948e15bc Giorgos Korfiatis
            try:
146 948e15bc Giorgos Korfiatis
                th = holdings[key]
147 948e15bc Giorgos Korfiatis
            except KeyError:
148 948e15bc Giorgos Korfiatis
                m = ("There is no such holding %s" % str(key))
149 948e15bc Giorgos Korfiatis
                provision = _mkProvision(key, quantity)
150 948e15bc Giorgos Korfiatis
                raise NoHoldingError(m,
151 948e15bc Giorgos Korfiatis
                                     provision=provision)
152 948e15bc Giorgos Korfiatis
153 948e15bc Giorgos Korfiatis
            if quantity >= 0:
154 948e15bc Giorgos Korfiatis
                operations.prepare(Import, th, quantity, force)
155 948e15bc Giorgos Korfiatis
156 948e15bc Giorgos Korfiatis
            else:  # release
157 948e15bc Giorgos Korfiatis
                abs_quantity = -quantity
158 948e15bc Giorgos Korfiatis
                operations.prepare(Release, th, abs_quantity, force)
159 948e15bc Giorgos Korfiatis
160 948e15bc Giorgos Korfiatis
            holdings[key] = th
161 948e15bc Giorgos Korfiatis
            provisions_to_create.append((key, quantity))
162 948e15bc Giorgos Korfiatis
163 948e15bc Giorgos Korfiatis
    except QuotaholderError:
164 948e15bc Giorgos Korfiatis
        operations.revert()
165 948e15bc Giorgos Korfiatis
        raise
166 948e15bc Giorgos Korfiatis
167 3679f852 Giorgos Korfiatis
    commission = Commission.objects.create(clientkey=clientkey,
168 3679f852 Giorgos Korfiatis
                                           name=name,
169 3679f852 Giorgos Korfiatis
                                           issue_time=now())
170 948e15bc Giorgos Korfiatis
    for (holder, source, resource), quantity in provisions_to_create:
171 948e15bc Giorgos Korfiatis
        Provision.objects.create(serial=commission,
172 948e15bc Giorgos Korfiatis
                                 holder=holder,
173 948e15bc Giorgos Korfiatis
                                 source=source,
174 948e15bc Giorgos Korfiatis
                                 resource=resource,
175 948e15bc Giorgos Korfiatis
                                 quantity=quantity)
176 948e15bc Giorgos Korfiatis
177 948e15bc Giorgos Korfiatis
    return commission.serial
178 948e15bc Giorgos Korfiatis
179 948e15bc Giorgos Korfiatis
180 948e15bc Giorgos Korfiatis
def _log_provision(commission, provision, holding, log_time, reason):
181 948e15bc Giorgos Korfiatis
182 948e15bc Giorgos Korfiatis
    kwargs = {
183 948e15bc Giorgos Korfiatis
        'serial':              commission.serial,
184 948e15bc Giorgos Korfiatis
        'name':                commission.name,
185 948e15bc Giorgos Korfiatis
        'holder':              holding.holder,
186 948e15bc Giorgos Korfiatis
        'source':              holding.source,
187 948e15bc Giorgos Korfiatis
        'resource':            holding.resource,
188 948e15bc Giorgos Korfiatis
        'limit':               holding.limit,
189 68b991bc Giorgos Korfiatis
        'usage_min':           holding.usage_min,
190 68b991bc Giorgos Korfiatis
        'usage_max':           holding.usage_max,
191 948e15bc Giorgos Korfiatis
        'delta_quantity':      provision.quantity,
192 948e15bc Giorgos Korfiatis
        'issue_time':          commission.issue_time,
193 948e15bc Giorgos Korfiatis
        'log_time':            log_time,
194 948e15bc Giorgos Korfiatis
        'reason':              reason,
195 948e15bc Giorgos Korfiatis
    }
196 948e15bc Giorgos Korfiatis
197 948e15bc Giorgos Korfiatis
    ProvisionLog.objects.create(**kwargs)
198 948e15bc Giorgos Korfiatis
199 948e15bc Giorgos Korfiatis
200 948e15bc Giorgos Korfiatis
def _get_commissions_for_update(clientkey, serials):
201 948e15bc Giorgos Korfiatis
    cs = Commission.objects.filter(
202 948e15bc Giorgos Korfiatis
        clientkey=clientkey, serial__in=serials).select_for_update()
203 948e15bc Giorgos Korfiatis
204 948e15bc Giorgos Korfiatis
    commissions = {}
205 948e15bc Giorgos Korfiatis
    for c in cs:
206 948e15bc Giorgos Korfiatis
        commissions[c.serial] = c
207 948e15bc Giorgos Korfiatis
    return commissions
208 948e15bc Giorgos Korfiatis
209 948e15bc Giorgos Korfiatis
210 948e15bc Giorgos Korfiatis
def _partition_by(f, l):
211 948e15bc Giorgos Korfiatis
    d = {}
212 948e15bc Giorgos Korfiatis
    for x in l:
213 948e15bc Giorgos Korfiatis
        group = f(x)
214 948e15bc Giorgos Korfiatis
        group_l = d.get(group, [])
215 948e15bc Giorgos Korfiatis
        group_l.append(x)
216 948e15bc Giorgos Korfiatis
        d[group] = group_l
217 948e15bc Giorgos Korfiatis
    return d
218 948e15bc Giorgos Korfiatis
219 948e15bc Giorgos Korfiatis
220 643b3d82 Giorgos Korfiatis
def resolve_pending_commissions(clientkey, accept_set=None, reject_set=None,
221 948e15bc Giorgos Korfiatis
                                reason=''):
222 643b3d82 Giorgos Korfiatis
    if accept_set is None:
223 643b3d82 Giorgos Korfiatis
        accept_set = []
224 643b3d82 Giorgos Korfiatis
    if reject_set is None:
225 643b3d82 Giorgos Korfiatis
        reject_set = []
226 643b3d82 Giorgos Korfiatis
227 948e15bc Giorgos Korfiatis
    actions = dict.fromkeys(accept_set, True)
228 948e15bc Giorgos Korfiatis
    conflicting = set()
229 948e15bc Giorgos Korfiatis
    for serial in reject_set:
230 948e15bc Giorgos Korfiatis
        if actions.get(serial) is True:
231 948e15bc Giorgos Korfiatis
            actions.pop(serial)
232 948e15bc Giorgos Korfiatis
            conflicting.add(serial)
233 948e15bc Giorgos Korfiatis
        else:
234 948e15bc Giorgos Korfiatis
            actions[serial] = False
235 948e15bc Giorgos Korfiatis
236 948e15bc Giorgos Korfiatis
    conflicting = list(conflicting)
237 948e15bc Giorgos Korfiatis
    serials = actions.keys()
238 948e15bc Giorgos Korfiatis
    commissions = _get_commissions_for_update(clientkey, serials)
239 948e15bc Giorgos Korfiatis
    ps = Provision.objects.filter(serial__in=serials).select_for_update()
240 948e15bc Giorgos Korfiatis
    holding_keys = sorted(p.holding_key() for p in ps)
241 948e15bc Giorgos Korfiatis
    holdings = _get_holdings_for_update(holding_keys)
242 948e15bc Giorgos Korfiatis
    provisions = _partition_by(lambda p: p.serial_id, ps)
243 948e15bc Giorgos Korfiatis
244 948e15bc Giorgos Korfiatis
    log_time = now()
245 948e15bc Giorgos Korfiatis
246 948e15bc Giorgos Korfiatis
    accepted, rejected, notFound = [], [], []
247 948e15bc Giorgos Korfiatis
    for serial, accept in actions.iteritems():
248 948e15bc Giorgos Korfiatis
        commission = commissions.get(serial)
249 948e15bc Giorgos Korfiatis
        if commission is None:
250 948e15bc Giorgos Korfiatis
            notFound.append(serial)
251 948e15bc Giorgos Korfiatis
            continue
252 948e15bc Giorgos Korfiatis
253 948e15bc Giorgos Korfiatis
        accepted.append(serial) if accept else rejected.append(serial)
254 948e15bc Giorgos Korfiatis
255 948e15bc Giorgos Korfiatis
        ps = provisions.get(serial)
256 948e15bc Giorgos Korfiatis
        assert ps is not None
257 948e15bc Giorgos Korfiatis
        for pv in ps:
258 948e15bc Giorgos Korfiatis
            key = pv.holding_key()
259 948e15bc Giorgos Korfiatis
            h = holdings.get(key)
260 948e15bc Giorgos Korfiatis
            if h is None:
261 948e15bc Giorgos Korfiatis
                raise CorruptedError("Corrupted provision")
262 948e15bc Giorgos Korfiatis
263 948e15bc Giorgos Korfiatis
            quantity = pv.quantity
264 948e15bc Giorgos Korfiatis
            action = finalize if accept else undo
265 948e15bc Giorgos Korfiatis
            if quantity >= 0:
266 948e15bc Giorgos Korfiatis
                action(Import, h, quantity)
267 948e15bc Giorgos Korfiatis
            else:  # release
268 948e15bc Giorgos Korfiatis
                action(Release, h, -quantity)
269 948e15bc Giorgos Korfiatis
270 948e15bc Giorgos Korfiatis
            prefix = 'ACCEPT:' if accept else 'REJECT:'
271 948e15bc Giorgos Korfiatis
            comm_reason = prefix + reason[-121:]
272 948e15bc Giorgos Korfiatis
            _log_provision(commission, pv, h, log_time, comm_reason)
273 948e15bc Giorgos Korfiatis
            pv.delete()
274 948e15bc Giorgos Korfiatis
        commission.delete()
275 948e15bc Giorgos Korfiatis
    return accepted, rejected, notFound, conflicting
276 948e15bc Giorgos Korfiatis
277 948e15bc Giorgos Korfiatis
278 948e15bc Giorgos Korfiatis
def resolve_pending_commission(clientkey, serial, accept=True):
279 948e15bc Giorgos Korfiatis
    if accept:
280 948e15bc Giorgos Korfiatis
        ok, notOk, notF, confl = resolve_pending_commissions(
281 948e15bc Giorgos Korfiatis
            clientkey=clientkey, accept_set=[serial])
282 948e15bc Giorgos Korfiatis
    else:
283 948e15bc Giorgos Korfiatis
        notOk, ok, notF, confl = resolve_pending_commissions(
284 948e15bc Giorgos Korfiatis
            clientkey=clientkey, reject_set=[serial])
285 948e15bc Giorgos Korfiatis
286 948e15bc Giorgos Korfiatis
    assert notOk == confl == []
287 948e15bc Giorgos Korfiatis
    assert ok + notF == [serial]
288 948e15bc Giorgos Korfiatis
    return bool(ok)
289 948e15bc Giorgos Korfiatis
290 948e15bc Giorgos Korfiatis
291 643b3d82 Giorgos Korfiatis
def get_pending_commissions(clientkey):
292 948e15bc Giorgos Korfiatis
    pending = Commission.objects.filter(clientkey=clientkey)
293 948e15bc Giorgos Korfiatis
    pending_list = pending.values_list('serial', flat=True)
294 948e15bc Giorgos Korfiatis
    return list(pending_list)
295 948e15bc Giorgos Korfiatis
296 948e15bc Giorgos Korfiatis
297 643b3d82 Giorgos Korfiatis
def get_commission(clientkey, serial):
298 948e15bc Giorgos Korfiatis
    try:
299 948e15bc Giorgos Korfiatis
        commission = Commission.objects.get(clientkey=clientkey,
300 948e15bc Giorgos Korfiatis
                                            serial=serial)
301 948e15bc Giorgos Korfiatis
    except Commission.DoesNotExist:
302 948e15bc Giorgos Korfiatis
        raise NoCommissionError(serial)
303 948e15bc Giorgos Korfiatis
304 948e15bc Giorgos Korfiatis
    objs = Provision.objects.select_related('holding')
305 948e15bc Giorgos Korfiatis
    provisions = objs.filter(serial=commission)
306 948e15bc Giorgos Korfiatis
307 948e15bc Giorgos Korfiatis
    ps = [p.todict() for p in provisions]
308 948e15bc Giorgos Korfiatis
309 948e15bc Giorgos Korfiatis
    response = {'serial':     serial,
310 948e15bc Giorgos Korfiatis
                'provisions': ps,
311 948e15bc Giorgos Korfiatis
                'issue_time': commission.issue_time,
312 3a1bed03 Giorgos Korfiatis
                'name':       commission.name,
313 948e15bc Giorgos Korfiatis
                }
314 948e15bc Giorgos Korfiatis
    return response