Example #1
0
 def open(self, resume=False):
     """Open the contest (connect to db, ...).  Must have been
     created before
     
     @param resume: Whether the contest must be resumed
     """
     if not os.path.isdir(self.directory):
         raise "No such contest '%s' exists - %s not found" % \
             (self.name, self.directory)
     self.dbproxy = SqliteDBProxy(self.dbpath)
     self.sm = SubmissionManager(self)
     if not resume:
         self.dbproxy.clear_submissions_sync()
Example #2
0
class Contest(object):
    """The contest.
    
    The server requires a (single) contest object.
    
    @ivar name:      Contest name
    @ivar directory: Contest directory
    @ivar dbpath:    Path to contest database
    @ivar avatars:   Dictionary of loggedin avatars (userid=>avatar object)
    @ivar duration:  Duration of the contest in seconds
    @ivar profile:   Contest Profile object
    @ivar ts_start:  Timestamp during start of contest
    @ivar dbproxy:   The database proxy object which has methods to manipulate
                     the database.
    @ivar dirs:      Special directories (name=>absolute path)
                     team - Team related files
                     judge - Judge's files
                     stat - Contest statistics related files
    """
    
    # Properties
    duration = property(lambda s: s._duration)
    profile = property(lambda s: s._profile)
    ts_start = property(lambda s: s._ts_start)
    
    def __init__(self, name, directory):
        """
        @param name         : name of the contest (dir name also)
        @param directory    : Parent directory of this contest directory
        """
        self.name = name
        self.directory = abspath(join(directory, name))
        self.dbpath = join(self.directory, 'repos', self.name)
        self.liveavatars = LiveAvatars()
        self._duration = None # If int, then time left to stop started contest
        self._profile = None  # Profile object
        self._ts_start = None # Timestamp during start of contest

        dirs = {}
        for dr in ['repos', 'team', 'judge', 'stat']:
            dirs[dr] = join(self.directory, dr)
        self.dirs = dirs

    def create(self, admin_passwd, admin_email):
        """Create the contest. This also opens it.
        
        @param admin_passwd: Password for 'admin' account
        @param admin_email:  Emailid of 'admin' account
        """
        if True in [x.isspace() for x in self.name]:
            raise ValueError, 'contest_name must not contain any white space'
        
        # Create directories
        dirs = [self.directory]             # Top-level
        dirs.extend(self.dirs.values())     # others ..
        [os.mkdir(dr) for dr in dirs]

        # Init DB
        dbpath = join(self.dirs['repos'], self.name) # Only one database
        db = sqlite.connect(dbpath, autocommit=True)
        cursor = db.cursor()
        queries = _get_queries(file(join(paths.DATA_DIR, 'contest-sqlite.sql')))
        [cursor.execute(query) for query in queries]
        cursor.close()
        db.close()


        # Open the contest and add admin account to db
        self.open()
        # We use *_sync method as we don't use Twisted's reactor yet!!
        self.dbproxy.add_user_sync('admin', admin_passwd, 'Contest Admin', 
                              admin_email, USER_ADMIN)
        
        
    def open(self, resume=False):
        """Open the contest (connect to db, ...).  Must have been
        created before
        
        @param resume: Whether the contest must be resumed
        """
        if not os.path.isdir(self.directory):
            raise "No such contest '%s' exists - %s not found" % \
                (self.name, self.directory)
        self.dbproxy = SqliteDBProxy(self.dbpath)
        self.sm = SubmissionManager(self)
        if not resume:
            self.dbproxy.clear_submissions_sync()

    def startServer(self, profile):
        """Start the contest server
        
        @param profile: ContestProfile object (see profile.__init__.py)
        """
        if not hasattr(self, 'dbproxy'):
            raise RuntimeError, 'Contest must be opened (contest.open) first'
        import gateway
        self._profile = profile
        profile.setContest(self)

        # DEBUG - start contest now
        self.startContest(60*100)

    def getContestAge(self):
        "Return number of seconds since start of contest"
        assert self.isrunning() is True, 'Contest is not running!!'
        return int(time.time()) - self._ts_start
    
    def startContest(self, duration):
        """Start accepting submissions

        @param duration: Duration (in seconds) before stopping the contest"""
        if self.isrunning():
            return
        self._duration = duration
        self._ts_start = int(time.time())
        reactor.callLater(duration, self.stopContest)
        for avatarId, avatar in self.liveavatars.items():
            avatar.contestStarted()
        log.info('Contest started with duration=%d seconds' % duration)
        
    def stopContest(self):
        """stop the contest"""
        if self.isrunning() is False:
            return
        self._duration = None
        self._ts_start = None
        # Tell avatars
        for avatarId, avatar in self.liveavatars.items():
            avatar.contestStopped()
        log.info('Contest stopped')
        
    def isrunning(self):
        """Whether contest is running?"""
        return self._duration is not None
    
    def getTeamAvatars(self):
        "Return the list of all (DBid, Team AvatarIds) from database"
        def _cbGotUsers(teams):
            avatarIds = []
            for dbid, team in teams.items():
                avatarIds.append((dbid, team['userid']))
            return avatarIds
        d = self.dbproxy.users_get_all({'type': str(USER_TEAM)})
        return d.addCallback(_cbGotUsers)
    
    def copyFile(self, avatar, instantid, filename, filecontent):
        """Copy the file to user data directory within directory named 
        instantid 

        @param avatar:      The avatar object which needs the file
        @param instantid:   The instantid that differentiates with other
                            instances of same avatar (eg. timestamp)
        @param filename:    Filename to write
        @param filecontent: File contents

        @return:            Created filepath
        """
        # Create directory if doesn't exist
        instantid = str(instantid)
        userdir_parent = join(self.directory, avatar.whoami, avatar.userid)
        userdir = join(userdir_parent, instantid)

        # If the instantid is not unique, modify it by prepending it
        # with sequence number
        old_instantid, seq_num = instantid, 1
        while os.path.isdir(userdir):
            instantid = old_instantid + '-' + str(seq_num)
            userdir = join(userdir_parent, instantid)
            seq_num = seq_num + 1
        os.makedirs(userdir)

        # Create file named filename with content filecontent
        filename = join(userdir, filename)
        fh = file(filename, 'w')
        try:
            fh.write(filecontent)
        finally:
            fh.close()
        return filename