[5080] | 1 | :mod:`waeup.sirp.accesscodes.accesscodes` -- access codes (aka PINs) |
---|
| 2 | ******************************************************************** |
---|
[5068] | 3 | |
---|
[5080] | 4 | .. module:: waeup.sirp.accesscodes.accesscodes |
---|
[5068] | 5 | |
---|
[5080] | 6 | Components that represent access codes and related. |
---|
[5068] | 7 | |
---|
[5080] | 8 | .. :doctest: |
---|
[5138] | 9 | .. :layer: waeup.sirp.testing.WAeUPSIRPUnitTestLayer |
---|
[5068] | 10 | |
---|
[5118] | 11 | About access-codes |
---|
| 12 | ================== |
---|
[5068] | 13 | |
---|
[5118] | 14 | Access codes are ids used to grant first-time access to the system for |
---|
| 15 | students. |
---|
[5080] | 16 | |
---|
[5118] | 17 | They are normally not generated by third-party components but always |
---|
| 18 | part of batches. |
---|
[5068] | 19 | |
---|
[5118] | 20 | An access-code consists of three parts:: |
---|
[5087] | 21 | |
---|
[5118] | 22 | APP-12-0123456789 |
---|
| 23 | ^^^ ^^ ^^^^^^^^^^ |
---|
| 24 | A B C |
---|
[5087] | 25 | |
---|
[5118] | 26 | where ``A`` tells about the purpose of the code, ``B`` gives the |
---|
| 27 | number of batch the code belongs to (1 to 3 digits), and ``C`` is a |
---|
| 28 | unique random number of 10 digits. |
---|
[5068] | 29 | |
---|
[5118] | 30 | For the generation of the random number :mod:`waeup.sirp` requires a |
---|
| 31 | 'urandom' entropy provider which is available with most standard |
---|
| 32 | Unix/Linux systems. This makes the generated numbers relatively |
---|
| 33 | secure, especially when compared with recent PHP-based applications. |
---|
[5068] | 34 | |
---|
| 35 | |
---|
[5118] | 36 | AccessCode |
---|
| 37 | ========== |
---|
[5068] | 38 | |
---|
[5118] | 39 | .. class:: AccessCode(batch_serial, random_num[,invalidation_date=None[, student_id=None]]) |
---|
[5068] | 40 | |
---|
[5118] | 41 | You normally shouldn't create standalone access-codes. Use |
---|
| 42 | instances of :class:`AccessCodeBatch` instead as they generate them |
---|
| 43 | (in masses) and care for them. |
---|
[5068] | 44 | |
---|
[5118] | 45 | Note, that :class:`AccessCode` instances are not persistent on |
---|
| 46 | themselves. They have to be stored inside a persistent object (like |
---|
| 47 | :class:`AccessCodeBatch`) to be kept. |
---|
[5080] | 48 | |
---|
[5150] | 49 | Access-codes can have three states: unused, used, and |
---|
| 50 | disabled. While still unused but enabled access-codes are reflected |
---|
| 51 | by an empty ``invalidation_date`` (set to ``None``), an already |
---|
| 52 | used (invalidated) code provides an invalidation date. |
---|
| 53 | |
---|
| 54 | In case of misuse or similar cases access-codes can also be |
---|
| 55 | completely disabled by setting the ``disabled`` attribute to |
---|
| 56 | ``True``. |
---|
| 57 | |
---|
[5118] | 58 | The class implements |
---|
| 59 | :mod:`waeup.sirp.accesscodes.interfaces.IAccessCode`: |
---|
[5068] | 60 | |
---|
[5118] | 61 | >>> from waeup.sirp.accesscodes.interfaces import IAccessCode |
---|
| 62 | >>> from waeup.sirp.accesscodes.accesscodes import AccessCode |
---|
[5068] | 63 | >>> from zope.interface.verify import verifyClass |
---|
| 64 | >>> verifyClass(IAccessCode, AccessCode) |
---|
| 65 | True |
---|
| 66 | |
---|
[5118] | 67 | .. attribute:: representation |
---|
[5109] | 68 | |
---|
[5118] | 69 | The 'full' id of an access-code as described above. Something |
---|
| 70 | like ``'APP-12-0123456789'``. |
---|
[5109] | 71 | |
---|
[5118] | 72 | Read-only attribute. |
---|
| 73 | |
---|
| 74 | .. attribute:: batch_serial |
---|
| 75 | |
---|
| 76 | Serial number of this access-code inside the batch. |
---|
| 77 | |
---|
| 78 | .. note:: XXX: Do we really need this? |
---|
| 79 | |
---|
| 80 | Subject to be dropped. |
---|
| 81 | |
---|
| 82 | .. attribute:: student_id |
---|
| 83 | |
---|
| 84 | A string or ``None``. Set when an access-code is |
---|
| 85 | invalidated. ``None`` by default. |
---|
| 86 | |
---|
| 87 | .. attribute:: batch_prefix |
---|
| 88 | |
---|
| 89 | The prefix of the batch the access-code belongs to. |
---|
| 90 | |
---|
| 91 | Read-only attribute. |
---|
| 92 | |
---|
| 93 | .. attribute:: batch_num |
---|
| 94 | |
---|
| 95 | The number of the batch the access-code belongs to. |
---|
| 96 | |
---|
| 97 | Read-only attribute. |
---|
| 98 | |
---|
| 99 | .. attribute:: cost |
---|
| 100 | |
---|
| 101 | What the access-code costs. A float. |
---|
| 102 | |
---|
| 103 | Read-only attribute. |
---|
| 104 | |
---|
| 105 | .. attribute:: invalidation_date |
---|
| 106 | |
---|
| 107 | Python datetime when the access code was invalidated, or |
---|
| 108 | ``None``. ``None`` by default. |
---|
| 109 | |
---|
[5150] | 110 | If an access-code is disabled, this attribute contains the |
---|
| 111 | datetime of disabling. |
---|
| 112 | |
---|
[5118] | 113 | Read-only attribute. Only batches are supposed to set this value. |
---|
| 114 | |
---|
[5150] | 115 | .. attribute:: disabled |
---|
| 116 | |
---|
| 117 | Boolean, ``False`` by default. When set to ``True``, this |
---|
| 118 | access-code should not be used any more. |
---|
| 119 | |
---|
| 120 | Read-only attribute. Only batches are supposed to set this value. |
---|
| 121 | |
---|
[5118] | 122 | Access codes that are not part of a batch, will give strange |
---|
| 123 | representations: |
---|
| 124 | |
---|
[5128] | 125 | >>> ac = AccessCode(None, '9999999999') |
---|
[5109] | 126 | >>> ac.representation |
---|
| 127 | '--<10-DIGITS>' |
---|
| 128 | |
---|
[5128] | 129 | Also the ``cost`` will not be set: |
---|
[5118] | 130 | |
---|
[5128] | 131 | >>> ac.cost is None |
---|
| 132 | True |
---|
| 133 | |
---|
| 134 | |
---|
[5118] | 135 | AccessCodeBatch |
---|
| 136 | =============== |
---|
| 137 | |
---|
| 138 | .. class:: AccessCodeBatch(creation_date, creator, batch_prefix, cost, entry_num, num) |
---|
| 139 | |
---|
| 140 | Create a batch of access-codes. |
---|
| 141 | |
---|
| 142 | :param creation_date: python datetime |
---|
| 143 | :param creator: creators user id |
---|
| 144 | :type creator: string |
---|
| 145 | :param batch_prefix: prefix of this batch |
---|
| 146 | :param cost: cost per access code |
---|
| 147 | :type cost: float |
---|
| 148 | :param entry_num: number of access codes to create |
---|
| 149 | :param num: number of this batch |
---|
| 150 | |
---|
| 151 | A persistent :class:`grok.Model`. It implements |
---|
| 152 | :class:`waeup.sirp.accesscodes.interfaces.IAccessCodeBatch`. |
---|
| 153 | |
---|
| 154 | When creating a batch, all entries (access-codes) are generated as |
---|
| 155 | well. |
---|
| 156 | |
---|
| 157 | .. attribute:: creation_date |
---|
| 158 | |
---|
| 159 | The datetime when the batch was created. |
---|
| 160 | |
---|
| 161 | .. attribute:: creator |
---|
| 162 | |
---|
| 163 | String with user id of the user that generated the batch. |
---|
| 164 | |
---|
| 165 | .. attribute:: cost |
---|
| 166 | |
---|
| 167 | Float representing the costs for a single access-code. All |
---|
| 168 | entries inside the batch share the same cost. |
---|
| 169 | |
---|
| 170 | .. attribute:: entry_num |
---|
| 171 | |
---|
| 172 | Number of entries (access-codes) inside the batch. |
---|
| 173 | |
---|
| 174 | .. attribute:: invalidated_num |
---|
| 175 | |
---|
| 176 | Number of entries that were already invalidated. |
---|
| 177 | |
---|
| 178 | .. attribute:: prefix |
---|
| 179 | |
---|
| 180 | Prefix of the batch. This tells about the purpose of this batch. |
---|
| 181 | |
---|
| 182 | .. attribute:: num |
---|
| 183 | |
---|
| 184 | Number of this batch. For a certain prefix there can exist |
---|
| 185 | several batches, which are numbered in increasing order. The |
---|
| 186 | number is normally computed by the |
---|
| 187 | :class:`AccessCodeBatchContainer` in which batches are |
---|
| 188 | stored. |
---|
| 189 | |
---|
| 190 | .. seealso:: :class:`AccessCodeBatchContainer` |
---|
| 191 | |
---|
| 192 | .. method:: entries() |
---|
| 193 | |
---|
| 194 | Get all accesscodes stored in the batch. |
---|
| 195 | |
---|
| 196 | Returns a generator over all stored entries. |
---|
| 197 | |
---|
| 198 | .. method:: getAccessCode(acesscode_id) |
---|
| 199 | |
---|
| 200 | Get the :class:`AccessCode` object for the given |
---|
| 201 | ``accesscode_id``. |
---|
| 202 | |
---|
| 203 | Certain single access codes can be accessed inside a batch by |
---|
| 204 | their representation (i.e. something like ``'APP-12-0123456789'``. |
---|
| 205 | |
---|
| 206 | When a code cannot be found :exc:`KeyError` is raised. |
---|
| 207 | |
---|
[5150] | 208 | .. method:: getAccessCodeForStudentId(student_id) |
---|
| 209 | |
---|
| 210 | Get the :class:`AccessCode` object for the given |
---|
| 211 | ``student_id``. |
---|
| 212 | |
---|
| 213 | Certain single access codes can be accessed inside a batch by |
---|
| 214 | their student_id. The student_id must be some string; ``None`` |
---|
| 215 | is not a valid value. |
---|
| 216 | |
---|
| 217 | When a code cannot be found :exc:`KeyError` is raised. |
---|
| 218 | |
---|
[5122] | 219 | .. method:: addAccessCode(serial_num, random_num) |
---|
| 220 | |
---|
| 221 | Add an access code to the batch. |
---|
| 222 | |
---|
| 223 | ``serial_num`` denotes the serial number of the new access-code |
---|
| 224 | inside the batch. ``random_num`` is a string of 10 digits unique |
---|
| 225 | in this batch. |
---|
| 226 | |
---|
[5118] | 227 | .. method:: invalidate(ac_id[, student_id=None]) |
---|
| 228 | |
---|
| 229 | Invalidate the access-code with ID ``ac_id``. |
---|
| 230 | |
---|
| 231 | Sets also the ``student_id`` attribute of the respective |
---|
| 232 | :class:`AccessCode` entry. |
---|
| 233 | |
---|
[5150] | 234 | .. method:: disable(ac_id, user_id) |
---|
| 235 | |
---|
| 236 | Disable the access-code with ID ``ac_id``. |
---|
| 237 | |
---|
| 238 | ``user_id`` is the user ID of the user triggering the |
---|
| 239 | process. Already disabled ACs are left untouched. |
---|
| 240 | |
---|
| 241 | Sets also the ``student_id`` and ``invalidation_date`` |
---|
| 242 | attributes of the respective :class:`AccessCode` entry. While |
---|
| 243 | ``student_id`` is set to the given ``user_id``, |
---|
| 244 | ``invalidation_date`` is set to current datetime. |
---|
| 245 | |
---|
| 246 | Disabled access codes are supposed not to be used any more at |
---|
| 247 | all. |
---|
| 248 | |
---|
| 249 | .. method:: enable(ac_id) |
---|
| 250 | |
---|
| 251 | (Re-)enable the access-code with ID ``ac_id``. |
---|
| 252 | |
---|
| 253 | This leaves the given AC in state ``unused``. Already enabled |
---|
| 254 | ACs are left untouched. |
---|
| 255 | |
---|
| 256 | Sets ``student_id`` and ``invalidation_date`` values of the |
---|
| 257 | respective :class:`AccessCode` entry to ``None``. |
---|
| 258 | |
---|
[5118] | 259 | .. method:: createCSVLogFile() |
---|
| 260 | |
---|
| 261 | Create a CSV file with data in batch. |
---|
| 262 | |
---|
| 263 | Data will not contain invalidation date nor student ids. File |
---|
| 264 | will be created in ``accesscodes`` subdir of data center storage |
---|
| 265 | path. |
---|
| 266 | |
---|
| 267 | Returns name of created file. |
---|
| 268 | |
---|
| 269 | .. method:: archive() |
---|
| 270 | |
---|
| 271 | Create a CSV file for archive. Archive files contain also |
---|
| 272 | ``student_id`` and ``invalidation_date``. |
---|
| 273 | |
---|
| 274 | Returns name of created file. |
---|
| 275 | |
---|
[5150] | 276 | .. method:: search(searchterm, searchtype) |
---|
[5118] | 277 | |
---|
[5150] | 278 | Search the batch for entries that comply with ``searchterm`` and |
---|
| 279 | ``searchtype``. |
---|
| 280 | |
---|
| 281 | ``searchtype`` must be one of ``serial``, ``pin``, or |
---|
| 282 | ``stud_id`` and specifies, what kind of data is contained in the |
---|
| 283 | ``searchterm``. |
---|
| 284 | |
---|
| 285 | - ``serial`` looks for the AC with the ``serial_batch`` set to |
---|
| 286 | the given (integer) number in ``searchterm``. Here |
---|
| 287 | ``searchterm`` must be an int number. |
---|
| 288 | |
---|
| 289 | - ``pin`` looks for the AC with the ``representation`` set to |
---|
| 290 | the given string in ``searchterm``. Here ``searchterm`` must |
---|
| 291 | be a string containing the full AC-ID like |
---|
| 292 | ``APP-1-0123456789``. |
---|
| 293 | |
---|
| 294 | - ``stud_id`` looks for the AC with the ``student_id`` set to |
---|
| 295 | the given string in ``searchterm``. Here ``searchterm`` must |
---|
| 296 | be a string containing the full student ID. |
---|
| 297 | |
---|
| 298 | Lookup is done via local BTrees and therefore pretty fast. |
---|
| 299 | |
---|
| 300 | Returns a list of access-codes found or empty list. |
---|
| 301 | |
---|
| 302 | |
---|
[5118] | 303 | Examples |
---|
| 304 | -------- |
---|
| 305 | |
---|
| 306 | :class:`AccessCodeBatch` implements :class:`IAccessCodeBatch`: |
---|
| 307 | |
---|
| 308 | >>> from waeup.sirp.accesscodes.interfaces import IAccessCodeBatch |
---|
| 309 | >>> from waeup.sirp.accesscodes.accesscodes import AccessCodeBatch |
---|
| 310 | >>> from zope.interface.verify import verifyClass |
---|
| 311 | >>> verifyClass(IAccessCodeBatch, AccessCodeBatch) |
---|
| 312 | True |
---|
| 313 | |
---|
| 314 | Creating a batch of three access-codes, with a cost of ``12.12`` per |
---|
| 315 | code, the batch prefix ``APP``, batch number ``10``, creator ID |
---|
| 316 | ``Fred`` and some arbitrary creation date: |
---|
| 317 | |
---|
| 318 | >>> import datetime |
---|
| 319 | >>> from waeup.sirp.accesscodes.accesscodes import AccessCodeBatch |
---|
| 320 | >>> batch = AccessCodeBatch( |
---|
| 321 | ... datetime.datetime(2009, 12, 23), 'Fred','APP', 12.12, 3, num=10) |
---|
| 322 | |
---|
| 323 | Getting all access-codes from a batch: |
---|
| 324 | |
---|
| 325 | >>> ac_codes = batch.entries() |
---|
| 326 | >>> ac_codes |
---|
| 327 | <generator object at 0x...> |
---|
| 328 | |
---|
| 329 | >>> [x.representation for x in ac_codes] |
---|
| 330 | ['APP-10-<10-DIGITS>', 'APP-10-<10-DIGITS>', 'APP-10-<10-DIGITS>'] |
---|
| 331 | |
---|
| 332 | >>> [x for x in batch.entries()] |
---|
| 333 | [<waeup.sirp...AccessCode object at 0x...>, ...] |
---|
| 334 | |
---|
| 335 | Getting a single entry from the batch: |
---|
| 336 | |
---|
| 337 | >>> ac_id = list(batch.entries())[0].representation |
---|
| 338 | >>> ac = batch.getAccessCode(ac_id) |
---|
| 339 | >>> ac |
---|
| 340 | <waeup.sirp...AccessCode object at 0x...> |
---|
| 341 | |
---|
| 342 | >>> ac is list(batch.entries())[0] |
---|
| 343 | True |
---|
| 344 | |
---|
| 345 | Trying to get a single not-existent entry from a batch: |
---|
| 346 | |
---|
| 347 | >>> batch.getAccessCode('blah') |
---|
| 348 | Traceback (most recent call last): |
---|
| 349 | ... |
---|
| 350 | KeyError: 'blah' |
---|
| 351 | |
---|
| 352 | Invalidating an entry: |
---|
| 353 | |
---|
| 354 | >>> batch.invalidated_num |
---|
| 355 | 0 |
---|
| 356 | |
---|
| 357 | >>> str(ac.invalidation_date), str(ac.student_id) |
---|
| 358 | ('None', 'None') |
---|
| 359 | |
---|
| 360 | >>> batch.invalidate(ac_id) |
---|
| 361 | >>> batch.invalidated_num |
---|
| 362 | 1 |
---|
| 363 | |
---|
| 364 | >>> ac.invalidation_date , str(ac.student_id) |
---|
| 365 | (datetime.datetime(...), 'None') |
---|
| 366 | |
---|
| 367 | >>> batch.invalidate(ac_id, 'some_user_id') |
---|
| 368 | >>> ac.student_id |
---|
| 369 | 'some_user_id' |
---|
| 370 | |
---|
[5150] | 371 | Getting a single entry by student_id: |
---|
| 372 | |
---|
| 373 | >>> batch.getAccessCodeForStudentId('some_user_id') |
---|
| 374 | <waeup.sirp...AccessCode object at 0x...> |
---|
| 375 | |
---|
| 376 | Non-existent values will cause a :exc:`KeyError`: |
---|
| 377 | |
---|
| 378 | >>> batch.getAccessCodeForStudentId('non-existing') |
---|
| 379 | Traceback (most recent call last): |
---|
| 380 | ... |
---|
| 381 | KeyError: 'non-existing' |
---|
| 382 | |
---|
| 383 | Already enabled entries will be left untouched when trying to renable |
---|
| 384 | them: |
---|
| 385 | |
---|
| 386 | >>> batch.enable(ac_id) |
---|
| 387 | >>> ac.student_id |
---|
| 388 | 'some_user_id' |
---|
| 389 | |
---|
| 390 | Disabling an entry: |
---|
| 391 | |
---|
| 392 | >>> batch.disabled_num |
---|
| 393 | 0 |
---|
| 394 | |
---|
| 395 | >>> ac.disabled |
---|
| 396 | False |
---|
| 397 | |
---|
| 398 | >>> batch.disable(ac_id, 'some userid') |
---|
| 399 | >>> ac.disabled |
---|
| 400 | True |
---|
| 401 | |
---|
| 402 | >>> ac.student_id |
---|
| 403 | 'some userid' |
---|
| 404 | |
---|
| 405 | >>> batch.disabled_num |
---|
| 406 | 1 |
---|
| 407 | |
---|
| 408 | Already disabled entries will not be disabled again: |
---|
| 409 | |
---|
| 410 | >>> batch.disable(ac_id, 'other userid') |
---|
| 411 | >>> ac.student_id |
---|
| 412 | 'some userid' |
---|
| 413 | |
---|
| 414 | Reenabling an entry: |
---|
| 415 | |
---|
| 416 | >>> batch.enable(ac_id) |
---|
| 417 | >>> ac.disabled |
---|
| 418 | False |
---|
| 419 | |
---|
| 420 | >>> ac.student_id is None |
---|
| 421 | True |
---|
| 422 | |
---|
| 423 | >>> ac.invalidation_date is None |
---|
| 424 | True |
---|
| 425 | |
---|
[5122] | 426 | Access codes get their ``cost`` from the batch they belong to. Note, |
---|
| 427 | that it is advisable to print costs always using a format, as Python |
---|
| 428 | floats are often represented by irritating values: |
---|
[5118] | 429 | |
---|
[5122] | 430 | >>> ac.cost |
---|
| 431 | 12.119999999999999 |
---|
| 432 | |
---|
| 433 | >>> print "%0.2f" % ac.cost |
---|
| 434 | 12.12 |
---|
| 435 | |
---|
[5150] | 436 | Searching for serials: |
---|
[5122] | 437 | |
---|
[5150] | 438 | >>> result = batch.search(0, 'serial') |
---|
| 439 | >>> result |
---|
| 440 | [<waeup.sirp...AccessCode object at 0x...>] |
---|
| 441 | |
---|
| 442 | >>> result[0].batch_serial |
---|
| 443 | 0 |
---|
| 444 | |
---|
| 445 | Searching for AC-IDs: |
---|
| 446 | |
---|
| 447 | >>> result = batch.search(ac.representation, 'pin') |
---|
| 448 | >>> result[0].representation |
---|
| 449 | 'APP-...-...' |
---|
| 450 | |
---|
| 451 | Searching for student IDs: |
---|
| 452 | |
---|
| 453 | >>> batch.invalidate(ac_id, 'some_new_user_id') |
---|
| 454 | >>> result = batch.search('some_new_user_id', 'stud_id') |
---|
| 455 | >>> result[0].student_id |
---|
| 456 | 'some_new_user_id' |
---|
| 457 | |
---|
| 458 | Searching for not existing entries will return empty lists: |
---|
| 459 | |
---|
| 460 | >>> batch.search(12, 'serial') |
---|
| 461 | [] |
---|
| 462 | |
---|
| 463 | >>> batch.search('not-a-valid-pin', 'pin') |
---|
| 464 | [] |
---|
| 465 | |
---|
| 466 | >>> batch.search('not-a-student-id', 'stud_id') |
---|
| 467 | [] |
---|
| 468 | |
---|
| 469 | >>> batch.search('blah', 'not-a-valid-searchtype') |
---|
| 470 | [] |
---|
| 471 | |
---|
[5122] | 472 | AccessCodeBatchContainer |
---|
| 473 | ======================== |
---|
| 474 | |
---|
| 475 | .. class:: AccessCodeBatchContainer() |
---|
| 476 | |
---|
| 477 | A container for access code batches. |
---|
| 478 | |
---|
| 479 | .. method:: addBatch(batch) |
---|
| 480 | |
---|
[5128] | 481 | Add a batch in this container. You should make sure, that |
---|
| 482 | entries in the given batch are set correctly. |
---|
[5122] | 483 | |
---|
[5128] | 484 | .. method:: createBatch(creation_date, creator, batch_prefix, cost, entry_num) |
---|
| 485 | |
---|
| 486 | Create a batch inside this container. Returns the batch created. |
---|
| 487 | |
---|
| 488 | :param creation_date: python datetime |
---|
| 489 | :param creator: creators user id |
---|
| 490 | :type creator: string |
---|
| 491 | :param batch_prefix: prefix of this batch |
---|
| 492 | :param cost: cost per access code |
---|
| 493 | :type cost: float |
---|
| 494 | :param entry_num: number of access codes to create |
---|
| 495 | |
---|
[5122] | 496 | .. method:: getNum(prefix) |
---|
| 497 | |
---|
| 498 | Get next unused num for a new batch and a given prefix. |
---|
| 499 | |
---|
| 500 | Batches for a given prefix are numerated. Whenever a new batch |
---|
| 501 | is created and other batches inside the container already have |
---|
| 502 | the same prefix, the new one will get the lowest unused number. |
---|
| 503 | |
---|
[5132] | 504 | .. method:: getImportFiles() |
---|
[5122] | 505 | |
---|
[5132] | 506 | Get a list of basenames of available import files, suitable for |
---|
| 507 | feeding :meth:`reimport`. |
---|
| 508 | |
---|
| 509 | .. method:: reimport(filename, creator=u'UNKNOWN') |
---|
| 510 | |
---|
| 511 | Reimport a CSV log file of previously created AC batch. |
---|
| 512 | |
---|
| 513 | ``filename`` is the name (basename) of the file residing in the |
---|
| 514 | accesscode storage's ``import`` directory.``creator`` is the |
---|
| 515 | user ID of the current user. |
---|
| 516 | |
---|
[5150] | 517 | .. method:: search(search_term, search_type) |
---|
[5132] | 518 | |
---|
[5150] | 519 | Look in all contained batches for access codes that comply with |
---|
| 520 | the given parameters. |
---|
| 521 | |
---|
| 522 | ``search_type`` must be one of ``serial``, ``pin``, or |
---|
| 523 | ``stud_id`` and specifies, what kind of data is contained in the |
---|
| 524 | ``search_term``. |
---|
| 525 | |
---|
| 526 | - ``serial`` looks for the AC with the ``serial_batch`` set to |
---|
| 527 | the given (integer) number in ``search_term``. Here |
---|
| 528 | ``search_term`` can be an integer or a string. If it is a |
---|
| 529 | string it will be converted to an int. |
---|
| 530 | |
---|
| 531 | - ``pin`` looks for the AC with the ``representation`` set to |
---|
| 532 | the given string in ``search_term``. Here ``search_term`` must |
---|
| 533 | be a string containing the full AC-ID like |
---|
| 534 | ``APP-1-0123456789``. |
---|
| 535 | |
---|
| 536 | - ``stud_id`` looks for the AC with the ``student_id`` set to |
---|
| 537 | the given string in ``search_term``. Here ``search_term`` must |
---|
| 538 | be a string containing the full student ID. |
---|
| 539 | |
---|
| 540 | Examples: |
---|
| 541 | --------- |
---|
| 542 | |
---|
| 543 | Creating a batch container: |
---|
| 544 | |
---|
| 545 | >>> from waeup.sirp.accesscodes.accesscodes import ( |
---|
| 546 | ... AccessCodeBatchContainer) |
---|
| 547 | >>> container = AccessCodeBatchContainer() |
---|
| 548 | |
---|
| 549 | Creating batches inside the container: |
---|
| 550 | |
---|
| 551 | >>> from datetime import datetime |
---|
| 552 | >>> batch1 = container.createBatch( |
---|
| 553 | ... datetime.now(), 'some userid', 'FOO', 10.12, 5) |
---|
| 554 | |
---|
| 555 | >>> batch2 = container.createBatch( |
---|
| 556 | ... datetime.now(), 'other userid', 'BAR', 1.95, 5) |
---|
| 557 | |
---|
| 558 | Searching the container for batch serials: |
---|
| 559 | |
---|
| 560 | >>> container.search(1, 'serial') |
---|
| 561 | [<waeup.sirp...AccessCode object at 0x...>, |
---|
| 562 | <waeup.sirp...AccessCode object at 0x...>] |
---|
| 563 | |
---|
| 564 | >>> container.search('not-a-number', 'serial') |
---|
| 565 | [] |
---|
| 566 | |
---|
| 567 | >>> result = container.search('1', 'serial') |
---|
| 568 | >>> result |
---|
| 569 | [<waeup.sirp...AccessCode object at 0x...>, |
---|
| 570 | <waeup.sirp...AccessCode object at 0x...>] |
---|
| 571 | |
---|
| 572 | Searching for ACs: |
---|
| 573 | |
---|
| 574 | >>> ac = result[0] |
---|
| 575 | >>> container.search(ac.representation, 'pin') |
---|
| 576 | [<waeup.sirp...AccessCode object at 0x...>] |
---|
| 577 | |
---|
| 578 | Searching for student IDs: |
---|
| 579 | |
---|
| 580 | >>> ac.__parent__.invalidate( |
---|
| 581 | ... ac.representation, 'some_user') |
---|
| 582 | >>> container.search('some_user', 'stud_id') |
---|
| 583 | [<waeup.sirp...AccessCode object at 0x...>] |
---|
| 584 | |
---|
| 585 | |
---|
[5109] | 586 | Access code plugin |
---|
| 587 | ================== |
---|
| 588 | |
---|
| 589 | .. class:: AccessCodePlugin |
---|
| 590 | |
---|
[5138] | 591 | A `waeup.sirp` plugin that updates existing WAeUP SIRP university |
---|
| 592 | instances so that they provide support for access-codes. |
---|
| 593 | |
---|
[5109] | 594 | .. attribute:: grok.implements(IWAeUPSIRPPluggable) |
---|
| 595 | .. attribute:: grok.name('accesscodes') |
---|
| 596 | |
---|
| 597 | .. method:: setup(site, name, logger) |
---|
| 598 | |
---|
| 599 | Create an accesscodebatch container in ``site``. Any events are |
---|
| 600 | logged to ``logger``. |
---|
| 601 | |
---|
| 602 | .. method:: update(site, name, logger) |
---|
| 603 | |
---|
| 604 | Check if ``site`` contains an accesscodebatch container and add |
---|
| 605 | it if missing. Any events are logged to ``logger``. |
---|
| 606 | |
---|
| 607 | The AccessCodePlugin is available as a global named utility for the |
---|
| 608 | IWAeUPSIRPPluggable interface named ``accesscodes``. |
---|
| 609 | |
---|
[5138] | 610 | It is looked up by a university instance when created, so that this |
---|
| 611 | instance has nothing to know about accesscodes at all and can |
---|
| 612 | although provide support for them. |
---|
[5109] | 613 | |
---|
| 614 | >>> from zope.component import getUtility |
---|
| 615 | >>> from waeup.sirp.interfaces import IWAeUPSIRPPluggable |
---|
| 616 | >>> plugin = getUtility(IWAeUPSIRPPluggable, name='accesscodes') |
---|
| 617 | >>> plugin |
---|
| 618 | <waeup.sirp.accesscodes.accesscodes.AccessCodePlugin object at 0x...> |
---|
| 619 | |
---|
| 620 | It provides a `setup()` and an `update()` method. Both have to be |
---|
| 621 | fed with a site object (the `IUniversity` instance to modify), a |
---|
| 622 | logger and a name. |
---|
| 623 | |
---|
| 624 | We create a faked site and a logger: |
---|
| 625 | |
---|
| 626 | >>> import logging |
---|
| 627 | >>> faked_site = dict() |
---|
| 628 | >>> logger = logging.getLogger('ac_test') |
---|
| 629 | >>> logger.setLevel(logging.DEBUG) |
---|
| 630 | >>> ch = logging.FileHandler('ac_tests.log', 'w') |
---|
| 631 | >>> ch.setLevel(logging.DEBUG) |
---|
| 632 | >>> logger.addHandler(ch) |
---|
| 633 | |
---|
| 634 | Now we can install our stuff in the faked site: |
---|
| 635 | |
---|
| 636 | >>> plugin.setup(faked_site, 'blah', logger) |
---|
| 637 | |
---|
| 638 | The faked site now has an access code container: |
---|
| 639 | |
---|
| 640 | >>> faked_site.keys() |
---|
| 641 | ['accesscodes'] |
---|
| 642 | |
---|
| 643 | The action is described in the log: |
---|
| 644 | |
---|
| 645 | >>> print open('ac_tests.log', 'r').read() |
---|
| 646 | Installed container for access code batches. |
---|
| 647 | |
---|
| 648 | We can also update an existing site, by calling `update()`: |
---|
| 649 | |
---|
| 650 | >>> plugin.update(faked_site, 'blah', logger) |
---|
| 651 | |
---|
| 652 | There was nothing to do for the updater: |
---|
| 653 | |
---|
| 654 | >>> print open('ac_tests.log', 'r').read() |
---|
| 655 | Installed container for access code batches. |
---|
| 656 | AccessCodePlugin: Updating site at {'accesscodes'...: Nothing to do. |
---|
| 657 | |
---|
| 658 | But if we remove the created batch container and call the updater, it |
---|
| 659 | will create a new one: |
---|
| 660 | |
---|
| 661 | >>> del faked_site['accesscodes'] |
---|
| 662 | >>> plugin.update(faked_site, 'blah', logger) |
---|
| 663 | >>> print open('ac_tests.log', 'r').read() |
---|
| 664 | Installed container for access code batches. |
---|
| 665 | AccessCodePlugin: Updating site at {'accesscodes'...: Nothing to do. |
---|
| 666 | Updating site at {}. Installing access codes. |
---|
| 667 | Installed container for access code batches. |
---|
| 668 | |
---|
| 669 | Clean up: |
---|
| 670 | |
---|
| 671 | >>> import os |
---|
| 672 | >>> os.unlink('ac_tests.log') |
---|