~vcs-imports/quotient/main

« back to all changes in this revision

Viewing changes to quotient/items/provisioner.py

  • Committer: glyph
  • Date: 2003-10-26 23:44:25 UTC
  • Revision ID: Arch-1:unnamed@bazaar.ubuntu.com%series--4208--patch-749
whitespace

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
 
2
 
# This module is part of the Quotient project and is Copyright 2003 Divmod:
3
 
# http://www.divmod.org/.  This is free software.  You can redistribute it
4
 
# and/or modify it under the terms of version 2.1 of the GNU Lesser General
5
 
# Public License as published by the Free Software Foundation.
6
 
 
7
 
from twisted.python.components import Interface, registerAdapter
8
 
from quotient.util import ItemForwardingComponent
9
 
 
10
 
class NotEnoughFuel(Exception):
11
 
    """I could not perform the task you asked for!
12
 
    """
13
 
 
14
 
class FuelTanker:
15
 
    """I am a tanker full of fuel.
16
 
    """
17
 
    # TODO: overdraft mode?
18
 
    def __init__(self):
19
 
        self.fuel = {}
20
 
        self.tankSizes = {}
21
 
 
22
 
    def addTanks(self, newTanks):
23
 
        """Add a dict of new 'full tank' values to this tanker.
24
 
        """
25
 
        self.tankSizes.update(newTanks)
26
 
        self.fuel.update(newTanks)
27
 
 
28
 
    def refuel(self):
29
 
        """This should be called during the monthly provision update tick.
30
 
        """
31
 
        # refuel
32
 
        self.fuel.clear()
33
 
        self.fuel.update(self.tankSizes)
34
 
 
35
 
    def deplete(self, tankName, amount, dontRaise=False):
36
 
        val = self.fuel[tankName] - amount
37
 
        if val <= 0:
38
 
            if dontRaise:
39
 
                return False
40
 
            else:
41
 
                raise NotEnoughFuel()
42
 
        else:
43
 
            self.fuel[tankName] = val
44
 
            return True
45
 
 
46
 
class Permissioner:
47
 
 
48
 
    gracePeriod = 1
49
 
    administrativeSuspension = 0
50
 
    
51
 
    def __init__(self):
52
 
        self.permissions = {}
53
 
 
54
 
    def tick(self):
55
 
        for k in self.permissions.keys():
56
 
            self.permissions[k] -= 1
57
 
            if not self.permissions[k]:
58
 
                del self.permissions[k]
59
 
 
60
 
    def check(self, permissionName, dontRaise=False):
61
 
        if self.permissions.get(permissionName) and not self.administrativeSuspension:
62
 
            return True
63
 
        elif dontRaise:
64
 
            return False
65
 
        raise NotEnoughFuel()
66
 
 
67
 
    def addPermission(self, newPermission, months):
68
 
        if self.permissions.has_key(newPermission):
69
 
            self.permissions[newPermission] += months
70
 
        else:
71
 
            self.permissions[newPermission] = months + self.gracePeriod
72
 
 
73
 
class FeaturePackage:
74
 
    """I am a collection of services that have been pre-paid over some amount of time.
75
 
    """
76
 
    def __init__(self, months):
77
 
        self.months = months
78
 
        self.permissions = []
79
 
        self.fueltanks = {}
80
 
 
81
 
    def addFeature(self, name, amount=0.):
82
 
        self.permissions.append(name)
83
 
        if amount:
84
 
            self.fueltanks[name] = amount
85
 
 
86
 
    def grantTo(self, prov):
87
 
        """Grant this feature package to a provisioner.
88
 
        """
89
 
        for permission in self.permissions:
90
 
            prov.permissioner.addPermission(permission, self.months)
91
 
        prov.fueltanker.addTanks(self.fueltanks)
92
 
 
93
 
class IProvisionable(Interface):
94
 
    property("tankID", doc="""
95
 
    Return an identifier for the tank which this object consumes from.
96
 
    """)
97
 
 
98
 
    property("startCost", doc="""
99
 
    Return the amount of fuel consumed from the tank identified by tankID
100
 
    before this provisionable will allow itself to be run, instantiated,
101
 
    installed, etc.
102
 
    """)
103
 
 
104
 
class IProvisioner(Interface):
105
 
    """Interface spec for Provisioner class.
106
 
    """
107
 
 
108
 
class Provisioner(Powerup):
109
 
    """I am a collection of knowledge about what services the user has paid
110
 
    for.
111
 
    
112
 
    TODO: alert the user when things are about to run out.
113
 
    """
114
 
 
115
 
    __implements__ = IProvisioner
116
 
 
117
 
    def __init__(self, store):
118
 
        self.permissioner = Permissioner()
119
 
        self.fueltanker = FuelTanker()
120
 
        Powerup.__init__(self, store)
121
 
 
122
 
    def setUpPools(self, avatar):
123
 
        avatar.setComponent(IProvisioner, FwdProvisioner(self.referenceTo()))
124
 
 
125
 
    def check(self, featureName, dontRaise=False):
126
 
        self.touch()
127
 
        return self.permissioner.check(featureName, dontRaise)
128
 
 
129
 
    def deplete(self, featureName, amount, dontRaise=False):
130
 
        self.touch()
131
 
        if self.check(featureName, dontRaise):
132
 
            return self.fueltanker.deplete(featureName, amount, dontRaise)
133
 
        elif dontRaise:
134
 
            return False
135
 
        raise NotEnoughFuel()
136
 
 
137
 
    def tick(self):
138
 
        """This should be called once per month.
139
 
        """
140
 
        self.touch()
141
 
        self.fueltanker.refuel()
142
 
        self.permissioner.tick()
143
 
 
144
 
    def provision(self, provisionable):
145
 
        self.touch()
146
 
        return self.deplete(provisionable.tankID, provisionable.startCost, False)
147
 
 
148
 
class FwdProvisioner(ItemForwardingComponent):
149
 
    __implements__ = IProvisioner
150
 
 
151
 
# Some canonical names.
152
 
 
153
 
class EPermission:
154
 
    """Collection of names of permissions (features which are set on an on/off
155
 
    basis).
156
 
    """
157
 
    
158
 
    IMAP4_LOGIN = "imap4-login"
159
 
    POP3_LOGIN = "pop3-login"
160
 
    WEB_LOGIN = "web-login"
161
 
    CALENDAR = "calendar"
162
 
    ADDRESSBOOK = "addressbook"
163
 
    SMTP_SEND = "smtp-send"
164
 
    
165
 
    TELEPHONY_SEND = "outgoing-calls"
166
 
    TELEPHONY_RECEIVE = "incoming-calls"
167
 
 
168
 
class ETank:
169
 
    """Collection of names of tanks (features which have a certain amount of
170
 
    monthly charge).
171
 
    """
172
 
 
173
 
    EXTRACTION = "extraction"
174
 
    CLASSIFICATION = "classification"
175
 
    INDEXING = "indexing"
176
 
    QUOTA = "quota"
177
 
 
178
 
oneYearService = FeaturePackage(12)
179
 
for permissionFeature in (EPermission.WEB_LOGIN,
180
 
                          EPermission.IMAP4_LOGIN,
181
 
                          EPermission.POP3_LOGIN,
182
 
                          EPermission.ADDRESSBOOK,
183
 
                          EPermission.SMTP_SEND):
184
 
    oneYearService.addFeature(permissionFeature)
185
 
 
186
 
for tankFeature in (ETank.EXTRACTION,
187
 
                    ETank.CLASSIFICATION,
188
 
                    ETank.INDEXING):
189
 
    # 20 CPU minutes per month for each feature
190
 
    oneYearService.addFeature(tankFeature, 20. * 60.)
191
 
 
192
 
# 50 MB plaintext storage per user per month - this is what SMTP should use (it
193
 
# is a soft limit)
194
 
oneYearService.addFeature(ETank.QUOTA, 1024 * 1024 * 50)
195
 
 
196
 
# 100 outgoing calls, unlimited incoming calls
197
 
oneYearService.addFeature(ETank.QUOTA, 100)
198
 
 
199
 
 
200
 
class ProvisionedPowerup(Powerup):
201
 
    dependencies = [Provisioner]
202
 
    def setUpPools(self, avatar):
203
 
        prov = IProvisioner(avatar)
204
 
        prov.provision(self)