~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to ivle/database.py

  • Committer: Nick Chadwick
  • Date: 2009-02-23 23:06:45 UTC
  • mto: (1099.1.227 exercise-ui)
  • mto: This revision was merged to the branch mainline in revision 1100.
  • Revision ID: chadnickbok@gmail.com-20090223230645-yam30bupsu0xj8dy
Modified the database layer so that exercises have a get_permissions
object.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# IVLE - Informatics Virtual Learning Environment
 
2
# Copyright (C) 2007-2009 The University of Melbourne
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
17
 
 
18
# Author: Matt Giuca, Will Grant
 
19
 
 
20
"""
 
21
Database Classes and Utilities for Storm ORM
 
22
 
 
23
This module provides all of the classes which map to database tables.
 
24
It also provides miscellaneous utility functions for database interaction.
 
25
"""
 
26
 
 
27
import md5
 
28
import datetime
 
29
 
 
30
from storm.locals import create_database, Store, Int, Unicode, DateTime, \
 
31
                         Reference, ReferenceSet, Bool, Storm, Desc
 
32
 
 
33
import ivle.conf
 
34
import ivle.caps
 
35
 
 
36
__all__ = ['get_store',
 
37
            'User',
 
38
            'Subject', 'Semester', 'Offering', 'Enrolment',
 
39
            'ProjectSet', 'Project', 'ProjectGroup', 'ProjectGroupMembership',
 
40
            'Exercise', 'Worksheet', 'WorksheetExercise',
 
41
            'ExerciseSave', 'ExerciseAttempt',
 
42
            'AlreadyEnrolledError', 'TestCase', 'TestSuite', 'TestSuiteVar'
 
43
        ]
 
44
 
 
45
def _kwarg_init(self, **kwargs):
 
46
    for k,v in kwargs.items():
 
47
        if k.startswith('_') or not hasattr(self.__class__, k):
 
48
            raise TypeError("%s got an unexpected keyword argument '%s'"
 
49
                % (self.__class__.__name__, k))
 
50
        setattr(self, k, v)
 
51
 
 
52
def get_conn_string():
 
53
    """
 
54
    Returns the Storm connection string, generated from the conf file.
 
55
    """
 
56
 
 
57
    clusterstr = ''
 
58
    if ivle.conf.db_user:
 
59
        clusterstr += ivle.conf.db_user
 
60
        if ivle.conf.db_password:
 
61
            clusterstr += ':' + ivle.conf.db_password
 
62
        clusterstr += '@'
 
63
 
 
64
    host = ivle.conf.db_host or 'localhost'
 
65
    port = ivle.conf.db_port or 5432
 
66
 
 
67
    clusterstr += '%s:%d' % (host, port)
 
68
 
 
69
    return "postgres://%s/%s" % (clusterstr, ivle.conf.db_dbname)
 
70
 
 
71
def get_store():
 
72
    """
 
73
    Open a database connection and transaction. Return a storm.store.Store
 
74
    instance connected to the configured IVLE database.
 
75
    """
 
76
    return Store(create_database(get_conn_string()))
 
77
 
 
78
# USERS #
 
79
 
 
80
class User(Storm):
 
81
    """
 
82
    Represents an IVLE user.
 
83
    """
 
84
    __storm_table__ = "login"
 
85
 
 
86
    id = Int(primary=True, name="loginid")
 
87
    login = Unicode()
 
88
    passhash = Unicode()
 
89
    state = Unicode()
 
90
    rolenm = Unicode()
 
91
    unixid = Int()
 
92
    nick = Unicode()
 
93
    pass_exp = DateTime()
 
94
    acct_exp = DateTime()
 
95
    last_login = DateTime()
 
96
    svn_pass = Unicode()
 
97
    email = Unicode()
 
98
    fullname = Unicode()
 
99
    studentid = Unicode()
 
100
    settings = Unicode()
 
101
 
 
102
    def _get_role(self):
 
103
        if self.rolenm is None:
 
104
            return None
 
105
        return ivle.caps.Role(self.rolenm)
 
106
    def _set_role(self, value):
 
107
        if not isinstance(value, ivle.caps.Role):
 
108
            raise TypeError("role must be an ivle.caps.Role")
 
109
        self.rolenm = unicode(value)
 
110
    role = property(_get_role, _set_role)
 
111
 
 
112
    __init__ = _kwarg_init
 
113
 
 
114
    def __repr__(self):
 
115
        return "<%s '%s'>" % (type(self).__name__, self.login)
 
116
 
 
117
    def authenticate(self, password):
 
118
        """Validate a given password against this user.
 
119
 
 
120
        Returns True if the given password matches the password hash for this
 
121
        User, False if it doesn't match, and None if there is no hash for the
 
122
        user.
 
123
        """
 
124
        if self.passhash is None:
 
125
            return None
 
126
        return self.hash_password(password) == self.passhash
 
127
 
 
128
    def hasCap(self, capability):
 
129
        """Given a capability (which is a Role object), returns True if this
 
130
        User has that capability, False otherwise.
 
131
        """
 
132
        return self.role.hasCap(capability)
 
133
 
 
134
    @property
 
135
    def password_expired(self):
 
136
        fieldval = self.pass_exp
 
137
        return fieldval is not None and datetime.datetime.now() > fieldval
 
138
 
 
139
    @property
 
140
    def account_expired(self):
 
141
        fieldval = self.acct_exp
 
142
        return fieldval is not None and datetime.datetime.now() > fieldval
 
143
 
 
144
    @property
 
145
    def valid(self):
 
146
        return self.state == 'enabled' and not self.account_expired
 
147
 
 
148
    def _get_enrolments(self, justactive):
 
149
        return Store.of(self).find(Enrolment,
 
150
            Enrolment.user_id == self.id,
 
151
            (Enrolment.active == True) if justactive else True,
 
152
            Enrolment.offering_id == Offering.id,
 
153
            Offering.semester_id == Semester.id,
 
154
            Offering.subject_id == Subject.id).order_by(
 
155
                Desc(Semester.year),
 
156
                Desc(Semester.semester),
 
157
                Desc(Subject.code)
 
158
            )
 
159
 
 
160
    def _set_password(self, password):
 
161
        if password is None:
 
162
            self.passhash = None
 
163
        else:
 
164
            self.passhash = unicode(User.hash_password(password))
 
165
    password = property(fset=_set_password)
 
166
 
 
167
    @property
 
168
    def subjects(self):
 
169
        return Store.of(self).find(Subject,
 
170
            Enrolment.user_id == self.id,
 
171
            Enrolment.active == True,
 
172
            Offering.id == Enrolment.offering_id,
 
173
            Subject.id == Offering.subject_id).config(distinct=True)
 
174
 
 
175
    # TODO: Invitations should be listed too?
 
176
    def get_groups(self, offering=None):
 
177
        preds = [
 
178
            ProjectGroupMembership.user_id == self.id,
 
179
            ProjectGroup.id == ProjectGroupMembership.project_group_id,
 
180
        ]
 
181
        if offering:
 
182
            preds.extend([
 
183
                ProjectSet.offering_id == offering.id,
 
184
                ProjectGroup.project_set_id == ProjectSet.id,
 
185
            ])
 
186
        return Store.of(self).find(ProjectGroup, *preds)
 
187
 
 
188
    @property
 
189
    def groups(self):
 
190
        return self.get_groups()
 
191
 
 
192
    @property
 
193
    def active_enrolments(self):
 
194
        '''A sanely ordered list of the user's active enrolments.'''
 
195
        return self._get_enrolments(True)
 
196
 
 
197
    @property
 
198
    def enrolments(self):
 
199
        '''A sanely ordered list of all of the user's enrolments.'''
 
200
        return self._get_enrolments(False) 
 
201
 
 
202
    @staticmethod
 
203
    def hash_password(password):
 
204
        return md5.md5(password).hexdigest()
 
205
 
 
206
    @classmethod
 
207
    def get_by_login(cls, store, login):
 
208
        """
 
209
        Get the User from the db associated with a given store and
 
210
        login.
 
211
        """
 
212
        return store.find(cls, cls.login == unicode(login)).one()
 
213
 
 
214
    def get_permissions(self, user):
 
215
        if user and user.rolenm == 'admin' or user is self:
 
216
            return set(['view', 'edit'])
 
217
        else:
 
218
            return set()
 
219
 
 
220
# SUBJECTS AND ENROLMENTS #
 
221
 
 
222
class Subject(Storm):
 
223
    __storm_table__ = "subject"
 
224
 
 
225
    id = Int(primary=True, name="subjectid")
 
226
    code = Unicode(name="subj_code")
 
227
    name = Unicode(name="subj_name")
 
228
    short_name = Unicode(name="subj_short_name")
 
229
    url = Unicode()
 
230
 
 
231
    offerings = ReferenceSet(id, 'Offering.subject_id')
 
232
 
 
233
    __init__ = _kwarg_init
 
234
 
 
235
    def __repr__(self):
 
236
        return "<%s '%s'>" % (type(self).__name__, self.short_name)
 
237
 
 
238
    def get_permissions(self, user):
 
239
        perms = set()
 
240
        if user is not None:
 
241
            perms.add('view')
 
242
            if user.rolenm == 'admin':
 
243
                perms.add('edit')
 
244
        return perms
 
245
 
 
246
class Semester(Storm):
 
247
    __storm_table__ = "semester"
 
248
 
 
249
    id = Int(primary=True, name="semesterid")
 
250
    year = Unicode()
 
251
    semester = Unicode()
 
252
    active = Bool()
 
253
 
 
254
    offerings = ReferenceSet(id, 'Offering.semester_id')
 
255
 
 
256
    __init__ = _kwarg_init
 
257
 
 
258
    def __repr__(self):
 
259
        return "<%s %s/%s>" % (type(self).__name__, self.year, self.semester)
 
260
 
 
261
class Offering(Storm):
 
262
    __storm_table__ = "offering"
 
263
 
 
264
    id = Int(primary=True, name="offeringid")
 
265
    subject_id = Int(name="subject")
 
266
    subject = Reference(subject_id, Subject.id)
 
267
    semester_id = Int(name="semesterid")
 
268
    semester = Reference(semester_id, Semester.id)
 
269
    groups_student_permissions = Unicode()
 
270
 
 
271
    enrolments = ReferenceSet(id, 'Enrolment.offering_id')
 
272
    members = ReferenceSet(id,
 
273
                           'Enrolment.offering_id',
 
274
                           'Enrolment.user_id',
 
275
                           'User.id')
 
276
    project_sets = ReferenceSet(id, 'ProjectSet.offering_id')
 
277
 
 
278
    worksheets = ReferenceSet(id, 
 
279
        'Worksheet.offering_id', 
 
280
        order_by="Worksheet.seq_no"
 
281
    )
 
282
 
 
283
    __init__ = _kwarg_init
 
284
 
 
285
    def __repr__(self):
 
286
        return "<%s %r in %r>" % (type(self).__name__, self.subject,
 
287
                                  self.semester)
 
288
 
 
289
    def enrol(self, user):
 
290
        '''Enrol a user in this offering.'''
 
291
        # We'll get a horrible database constraint violation error if we try
 
292
        # to add a second enrolment.
 
293
        if Store.of(self).find(Enrolment,
 
294
                               Enrolment.user_id == user.id,
 
295
                               Enrolment.offering_id == self.id).count() == 1:
 
296
            raise AlreadyEnrolledError()
 
297
 
 
298
        e = Enrolment(user=user, offering=self, active=True)
 
299
        self.enrolments.add(e)
 
300
 
 
301
    def get_permissions(self, user):
 
302
        perms = set()
 
303
        if user is not None:
 
304
            perms.add('view')
 
305
            if user.rolenm == 'admin':
 
306
                perms.add('edit')
 
307
        return perms
 
308
 
 
309
class Enrolment(Storm):
 
310
    __storm_table__ = "enrolment"
 
311
    __storm_primary__ = "user_id", "offering_id"
 
312
 
 
313
    user_id = Int(name="loginid")
 
314
    user = Reference(user_id, User.id)
 
315
    offering_id = Int(name="offeringid")
 
316
    offering = Reference(offering_id, Offering.id)
 
317
    notes = Unicode()
 
318
    active = Bool()
 
319
 
 
320
    @property
 
321
    def groups(self):
 
322
        return Store.of(self).find(ProjectGroup,
 
323
                ProjectSet.offering_id == self.offering.id,
 
324
                ProjectGroup.project_set_id == ProjectSet.id,
 
325
                ProjectGroupMembership.project_group_id == ProjectGroup.id,
 
326
                ProjectGroupMembership.user_id == self.user.id)
 
327
 
 
328
    __init__ = _kwarg_init
 
329
 
 
330
    def __repr__(self):
 
331
        return "<%s %r in %r>" % (type(self).__name__, self.user,
 
332
                                  self.offering)
 
333
 
 
334
class AlreadyEnrolledError(Exception):
 
335
    pass
 
336
 
 
337
# PROJECTS #
 
338
 
 
339
class ProjectSet(Storm):
 
340
    __storm_table__ = "project_set"
 
341
 
 
342
    id = Int(name="projectsetid", primary=True)
 
343
    offering_id = Int(name="offeringid")
 
344
    offering = Reference(offering_id, Offering.id)
 
345
    max_students_per_group = Int()
 
346
 
 
347
    projects = ReferenceSet(id, 'Project.project_set_id')
 
348
    project_groups = ReferenceSet(id, 'ProjectGroup.project_set_id')
 
349
 
 
350
    __init__ = _kwarg_init
 
351
 
 
352
    def __repr__(self):
 
353
        return "<%s %d in %r>" % (type(self).__name__, self.id,
 
354
                                  self.offering)
 
355
 
 
356
class Project(Storm):
 
357
    __storm_table__ = "project"
 
358
 
 
359
    id = Int(name="projectid", primary=True)
 
360
    synopsis = Unicode()
 
361
    url = Unicode()
 
362
    project_set_id = Int(name="projectsetid")
 
363
    project_set = Reference(project_set_id, ProjectSet.id)
 
364
    deadline = DateTime()
 
365
 
 
366
    __init__ = _kwarg_init
 
367
 
 
368
    def __repr__(self):
 
369
        return "<%s '%s' in %r>" % (type(self).__name__, self.synopsis,
 
370
                                  self.project_set.offering)
 
371
 
 
372
class ProjectGroup(Storm):
 
373
    __storm_table__ = "project_group"
 
374
 
 
375
    id = Int(name="groupid", primary=True)
 
376
    name = Unicode(name="groupnm")
 
377
    project_set_id = Int(name="projectsetid")
 
378
    project_set = Reference(project_set_id, ProjectSet.id)
 
379
    nick = Unicode()
 
380
    created_by_id = Int(name="createdby")
 
381
    created_by = Reference(created_by_id, User.id)
 
382
    epoch = DateTime()
 
383
 
 
384
    members = ReferenceSet(id,
 
385
                           "ProjectGroupMembership.project_group_id",
 
386
                           "ProjectGroupMembership.user_id",
 
387
                           "User.id")
 
388
 
 
389
    __init__ = _kwarg_init
 
390
 
 
391
    def __repr__(self):
 
392
        return "<%s %s in %r>" % (type(self).__name__, self.name,
 
393
                                  self.project_set.offering)
 
394
 
 
395
class ProjectGroupMembership(Storm):
 
396
    __storm_table__ = "group_member"
 
397
    __storm_primary__ = "user_id", "project_group_id"
 
398
 
 
399
    user_id = Int(name="loginid")
 
400
    user = Reference(user_id, User.id)
 
401
    project_group_id = Int(name="groupid")
 
402
    project_group = Reference(project_group_id, ProjectGroup.id)
 
403
 
 
404
    __init__ = _kwarg_init
 
405
 
 
406
    def __repr__(self):
 
407
        return "<%s %r in %r>" % (type(self).__name__, self.user,
 
408
                                  self.project_group)
 
409
 
 
410
# WORKSHEETS AND EXERCISES #
 
411
 
 
412
class Exercise(Storm):
 
413
    __storm_table__ = "exercise"
 
414
    id = Unicode(primary=True, name="identifier")
 
415
    name = Unicode()
 
416
    description = Unicode()
 
417
    partial = Unicode()
 
418
    solution = Unicode()
 
419
    include = Unicode()
 
420
    num_rows = Int()
 
421
 
 
422
    worksheets = ReferenceSet(id,
 
423
        'WorksheetExercise.exercise_id',
 
424
        'WorksheetExercise.worksheet_id',
 
425
        'Worksheet.id'
 
426
    )
 
427
    
 
428
    test_suites = ReferenceSet(id, 'TestSuite.exercise_id')
 
429
 
 
430
    __init__ = _kwarg_init
 
431
 
 
432
    def __repr__(self):
 
433
        return "<%s %s>" % (type(self).__name__, self.name)
 
434
 
 
435
    def get_permissions(self, user):
 
436
        perms = set()
 
437
        if user is not None:
 
438
            if user.rolenm == 'admin':
 
439
                perms.add('edit')
 
440
                perms.add('view')
 
441
        return perms
 
442
 
 
443
class Worksheet(Storm):
 
444
    __storm_table__ = "worksheet"
 
445
 
 
446
    id = Int(primary=True, name="worksheetid")
 
447
    offering_id = Int(name="offeringid")
 
448
    identifier = Unicode()
 
449
    name = Unicode()
 
450
    assessable = Bool()
 
451
    data = Unicode()
 
452
    seq_no = Int()
 
453
    format = Unicode()
 
454
 
 
455
    attempts = ReferenceSet(id, "ExerciseAttempt.worksheetid")
 
456
    offering = Reference(offering_id, 'Offering.id')
 
457
 
 
458
    # Use worksheet_exercises to get access to the WorksheetExercise objects
 
459
    # binding worksheets to exercises. This is required to access the
 
460
    # "optional" field.
 
461
    worksheet_exercises = ReferenceSet(id,
 
462
        'WorksheetExercise.worksheet_id')
 
463
        
 
464
 
 
465
    __init__ = _kwarg_init
 
466
 
 
467
    def __repr__(self):
 
468
        return "<%s %s>" % (type(self).__name__, self.name)
 
469
 
 
470
    # XXX Refactor this - make it an instance method of Subject rather than a
 
471
    # class method of Worksheet. Can't do that now because Subject isn't
 
472
    # linked referentially to the Worksheet.
 
473
    @classmethod
 
474
    def get_by_name(cls, store, subjectname, worksheetname):
 
475
        """
 
476
        Get the Worksheet from the db associated with a given store, subject
 
477
        name and worksheet name.
 
478
        """
 
479
        return store.find(cls, cls.subject == unicode(subjectname),
 
480
            cls.name == unicode(worksheetname)).one()
 
481
 
 
482
    def remove_all_exercises(self, store):
 
483
        """
 
484
        Remove all exercises from this worksheet.
 
485
        This does not delete the exercises themselves. It just removes them
 
486
        from the worksheet.
 
487
        """
 
488
        store.find(WorksheetExercise,
 
489
            WorksheetExercise.worksheet == self).remove()
 
490
            
 
491
    def get_permissions(self, user):
 
492
        return self.offering.get_permissions(user)
 
493
 
 
494
class WorksheetExercise(Storm):
 
495
    __storm_table__ = "worksheet_exercise"
 
496
    
 
497
    id = Int(primary=True, name="ws_ex_id")
 
498
 
 
499
    worksheet_id = Int(name="worksheetid")
 
500
    worksheet = Reference(worksheet_id, Worksheet.id)
 
501
    exercise_id = Unicode(name="exerciseid")
 
502
    exercise = Reference(exercise_id, Exercise.id)
 
503
    optional = Bool()
 
504
    active = Bool()
 
505
    seq_no = Int()
 
506
    
 
507
    saves = ReferenceSet(id, "ExerciseSave.ws_ex_id")
 
508
    attempts = ReferenceSet(id, "ExerciseAttempt.ws_ex_id")
 
509
 
 
510
    __init__ = _kwarg_init
 
511
 
 
512
    def __repr__(self):
 
513
        return "<%s %s in %s>" % (type(self).__name__, self.exercise.name,
 
514
                                  self.worksheet.identifier)
 
515
 
 
516
class ExerciseSave(Storm):
 
517
    """
 
518
    Represents a potential solution to an exercise that a user has submitted
 
519
    to the server for storage.
 
520
    A basic ExerciseSave is just the current saved text for this exercise for
 
521
    this user (doesn't count towards their attempts).
 
522
    ExerciseSave may be extended with additional semantics (such as
 
523
    ExerciseAttempt).
 
524
    """
 
525
    __storm_table__ = "exercise_save"
 
526
    __storm_primary__ = "ws_ex_id", "user_id"
 
527
 
 
528
    ws_ex_id = Int(name="ws_ex_id")
 
529
    worksheet_exercise = Reference(ws_ex_id, "WorksheetExercise.id")
 
530
 
 
531
    user_id = Int(name="loginid")
 
532
    user = Reference(user_id, User.id)
 
533
    date = DateTime()
 
534
    text = Unicode()
 
535
 
 
536
    __init__ = _kwarg_init
 
537
 
 
538
    def __repr__(self):
 
539
        return "<%s %s by %s at %s>" % (type(self).__name__,
 
540
            self.exercise.name, self.user.login, self.date.strftime("%c"))
 
541
 
 
542
class ExerciseAttempt(ExerciseSave):
 
543
    """
 
544
    An ExerciseAttempt is a special case of an ExerciseSave. Like an
 
545
    ExerciseSave, it constitutes exercise solution data that the user has
 
546
    submitted to the server for storage.
 
547
    In addition, it contains additional information about the submission.
 
548
    complete - True if this submission was successful, rendering this exercise
 
549
        complete for this user.
 
550
    active - True if this submission is "active" (usually true). Submissions
 
551
        may be de-activated by privileged users for special reasons, and then
 
552
        they won't count (either as a penalty or success), but will still be
 
553
        stored.
 
554
    """
 
555
    __storm_table__ = "exercise_attempt"
 
556
    __storm_primary__ = "ws_ex_id", "user_id", "date"
 
557
 
 
558
    # The "text" field is the same but has a different name in the DB table
 
559
    # for some reason.
 
560
    text = Unicode(name="attempt")
 
561
    complete = Bool()
 
562
    active = Bool()
 
563
    
 
564
    def get_permissions(self, user):
 
565
        return set(['view']) if user is self.user else set()
 
566
  
 
567
class TestSuite(Storm):
 
568
    """A Testsuite acts as a container for the test cases of an exercise."""
 
569
    __storm_table__ = "test_suite"
 
570
    __storm_primary__ = "exercise_id", "suiteid"
 
571
    
 
572
    suiteid = Int()
 
573
    exercise_id = Unicode(name="exerciseid")
 
574
    description = Unicode()
 
575
    seq_no = Int()
 
576
    function = Unicode()
 
577
    stdin = Unicode()
 
578
    exercise = Reference(exercise_id, Exercise.id)
 
579
    test_cases = ReferenceSet(suiteid, 'TestCase.suiteid')
 
580
    variables = ReferenceSet(suiteid, 'TestSuiteVar.suiteid')
 
581
 
 
582
class TestCase(Storm):
 
583
    """A TestCase is a member of a TestSuite.
 
584
    
 
585
    It contains the data necessary to check if an exercise is correct"""
 
586
    __storm_table__ = "test_case"
 
587
    __storm_primary__ = "testid", "suiteid"
 
588
    
 
589
    testid = Int()
 
590
    suiteid = Int()
 
591
    suite = Reference(suiteid, "TestSuite.suiteid")
 
592
    passmsg = Unicode()
 
593
    failmsg = Unicode()
 
594
    test_default = Unicode()
 
595
    seq_no = Int()
 
596
    
 
597
    parts = ReferenceSet(testid, "TestCasePart.testid")
 
598
    
 
599
    __init__ = _kwarg_init
 
600
 
 
601
class TestSuiteVar(Storm):
 
602
    """A container for the arguments of a Test Suite"""
 
603
    __storm_table__ = "suite_variable"
 
604
    __storm_primary__ = "varid"
 
605
    
 
606
    varid = Int()
 
607
    suiteid = Int()
 
608
    var_name = Unicode()
 
609
    var_value = Unicode()
 
610
    var_type = Unicode()
 
611
    arg_no = Int()
 
612
    
 
613
    suite = Reference(suiteid, "TestSuite.suiteid")
 
614
    
 
615
    __init__ = _kwarg_init
 
616
    
 
617
class TestCasePart(Storm):
 
618
    """A container for the test elements of a Test Case"""
 
619
    __storm_table__ = "test_case_part"
 
620
    __storm_primary__ = "partid"
 
621
    
 
622
    partid = Int()
 
623
    testid = Int()
 
624
    
 
625
    part_type = Unicode()
 
626
    test_type = Unicode()
 
627
    data = Unicode()
 
628
    filename = Unicode()
 
629
    
 
630
    test = Reference(testid, "TestCase.testid")
 
631
    
 
632
    __init__ = _kwarg_init