示例#1
0
    def asPlainText(self, data={}):
        if 'dry-run' in data:
            yield "*** DRY RUN ***\n"

        total_reclaimed = 0
        for (studentId, unclaimed) in Session.execute("""
SELECT a.studentId,
    SUM(coinsAwarded) -
    COALESCE((SELECT SUM(amount) FROM coinAward ca WHERE ca.studentId = a.studentId), 0)
    AS unclaimed
FROM answer a, student s
WHERE coinsAwarded > 0
AND a.studentId = s.studentId AND s.hostId = 1
AND a.timeEnd < CURDATE() - INTERVAL 2 YEAR
AND a.studentId NOT IN (SELECT DISTINCT c.studentId FROM coinAward c WHERE c.awardTime >= CURDATE() - INTERVAL 2 YEAR)
GROUP BY a.studentId
HAVING unclaimed > 0
        """):

            total_reclaimed += int(unclaimed)
            if 'dry-run' in data:
                txId = 'DRY_RUN'
            else:
                txId = coin.sendTransaction(EIAS_WALLET,
                                            unclaimed,
                                            message="Auto-reclaim of awards")
                # Worked, so update database
                Session.add(
                    db.CoinAward(
                        studentId=studentId,
                        amount=int(unclaimed),
                        walletId=EIAS_WALLET,
                        txId=txId,
                        awardTime=utcnow(
                        ),  # NB: So it gets mocked in the tests
                    ))
                Session.flush()
            yield "StudentId: %d Unclaimed: %d Transaction: %s\n" % (
                studentId,
                unclaimed,
                txId,
            )
        yield "Total: %d\n" % total_reclaimed
示例#2
0
def ingestData(data):
    idMap = collections.defaultdict(dict)
    inserts = {}

    # Check all host keys match our stored versions, and map to our IDs
    for host in data['host']:
        try:
            dbHost = Session.query(db.Host).filter_by(
                hostKey=host['hostKey'],
                fqdn=host['fqdn'],
            ).one()
        except NoResultFound:
            raise ValueError("Unknown host %s:%s, cannot import results" % (host['hostKey'], host['fqdn']))
        idMap['hostId'][host['hostId']] = dbHost.hostId

    # Map students to our IDs
    inserts['student'] = 0
    for student in data['student']:
        try:
            dbStudent = (Session.query(db.Student)
                .filter(db.Student.hostId == idMap['hostId'][student['hostId']])
                .filter(db.Student.userName == student['userName'])
                .one())
            dbStudent.userName = student['userName']
            dbStudent.email = student['eMail']
        except NoResultFound:
            dbStudent = db.Student(
                hostId=idMap['hostId'][student['hostId']],
                userName=student['userName'],
                eMail=student['eMail'],
            )
            Session.add(dbStudent)
            Session.flush()
            inserts['student'] += 1
        idMap['studentId'][student['studentId']] = dbStudent.studentId
    Session.flush()

    # Map questions to our IDs
    for question in data['question']:
        try:
            dbQuestionId = (Session.query(db.Question.questionId)
                .filter(db.Question.plonePath == question['plonePath'])
                .one())
        except NoResultFound:
            raise ValueError("Missing question at %s" % question['plonePath'])
        idMap['questionId'][question['questionId']] = dbQuestionId[0]
    Session.flush()

    # Map lectures to our IDs
    inserts['lecture'] = 0
    for lecture in data['lecture']:
        try:
            dbLecture = (Session.query(db.Lecture)
                .filter(db.Lecture.hostId == idMap['hostId'][lecture['hostId']])
                .filter(db.Lecture.plonePath == lecture['plonePath'])
                .one())
            dbLecture.plonePath = lecture['plonePath']
        except NoResultFound:
            dbLecture = db.Lecture(
                hostId=idMap['hostId'][lecture['hostId']],
                plonePath=lecture['plonePath'],
            )
            Session.add(dbLecture)
            Session.flush()
            inserts['lecture'] += 1
        idMap['lectureId'][lecture['lectureId']] = dbLecture.lectureId
    Session.flush()

    # Any answer entries we fetch should be at least as new as the oldest incoming entry
    minVal = None
    for a in data['answer']:
        if minVal is None or a['timeEnd'] < minVal:
            minVal = a['timeEnd']
    answerFilter = db.Answer.timeEnd.__ge__(datetime.datetime.utcfromtimestamp(minVal or 0))
    minVal = None
    for a in data['coin_award']:
        if minVal is None or a['awardTime'] < minVal:
            minVal = a['awardTime']
    coinAwardFilter = db.CoinAward.awardTime.__ge__(datetime.datetime.utcfromtimestamp(minVal or 0))

    if 'lecture_global_setting' in data:
        inserts['lecture_global_setting'] = 0
        for (dataEntry, dbEntry) in findMissingEntries(
                data['lecture_global_setting'],
                Session.query(db.LectureGlobalSetting)
                    .filter(db.LectureGlobalSetting.lectureId.in_(idMap['lectureId'].values()))
                    .order_by(
                        db.LectureGlobalSetting.lectureId,
                        db.LectureGlobalSetting.lectureVersion,
                        db.LectureGlobalSetting.key,
                    ),
                sortCols=['lectureId', 'lectureVersion', 'key'],
                idMap=idMap):
            dataEntry['creationDate'] = datetime.datetime.utcfromtimestamp(dataEntry['creationDate'])
            Session.add(db.LectureGlobalSetting(**dataEntry))
            inserts['lecture_global_setting'] += 1
        Session.flush()

    if 'lecture_student_setting' in data:
        inserts['lecture_student_setting'] = 0
        for (dataEntry, dbEntry) in findMissingEntries(
                data['lecture_student_setting'],
                Session.query(db.LectureStudentSetting)
                    .filter(db.LectureStudentSetting.lectureId.in_(idMap['lectureId'].values()))
                    .filter(db.LectureStudentSetting.studentId.in_(idMap['studentId'].values()))
                    .order_by(
                        db.LectureStudentSetting.lectureId,
                        db.LectureStudentSetting.lectureVersion,
                        db.LectureStudentSetting.studentId,
                        db.LectureStudentSetting.key,
                    ),
                sortCols=['lectureId', 'lectureVersion', 'studentId', 'key'],
                idMap=idMap):
            dataEntry['creationDate'] = datetime.datetime.utcfromtimestamp(dataEntry['creationDate'])
            Session.add(db.LectureStudentSetting(**dataEntry))
            inserts['lecture_student_setting'] += 1
        Session.flush()

    inserts['ug_question'] = 0
    for (dataEntry, dbEntry) in findMissingEntries(
            data['ug_question'],
            Session.query(db.UserGeneratedQuestion)
                .order_by(db.UserGeneratedQuestion.ugQuestionGuid),
            sortCols=['ugQuestionGuid'],
            ignoreCols=['ugQuestionId'],
            idMap=idMap):
        Session.add(db.UserGeneratedQuestion(**dataEntry))
        inserts['ug_question'] += 1
    Session.flush()

    inserts['ug_answer'] = 0
    for (dataEntry, dbEntry) in findMissingEntries(
            data['ug_answer'],
            Session.query(db.UserGeneratedAnswer)
                .filter(db.UserGeneratedAnswer.studentId.in_(idMap['studentId'].values()))
                .order_by(db.UserGeneratedAnswer.studentId, db.UserGeneratedAnswer.ugQuestionGuid),
            sortCols=['studentId', 'ugQuestionGuid'],
            ignoreCols=['ugAnswerId'],
            idMap=idMap):
        if dataEntry['studentId'] is not None:
            Session.add(db.UserGeneratedAnswer(**dataEntry))
            inserts['ug_answer'] += 1
        else:
            # If studentId is None, we've selected it but not relevant to current data
            # (the dump isn't selective enough). Ignore it, will import eventually
            continue
    Session.flush()

    # Filter out answer student/question/timeEnd combinations already stored in DB
    inserts['answer'] = 0
    for (dataEntry, dbEntry) in findMissingEntries(
            data['answer'],
            Session.query(db.Answer)
                .filter(db.Answer.lectureId.in_(idMap['lectureId'].values()))
                .filter(db.Answer.studentId.in_(idMap['studentId'].values()))
                .filter(answerFilter)
                .order_by(db.Answer.lectureId, db.Answer.studentId, db.Answer.timeEnd),
            sortCols=['lectureId', 'studentId', 'timeEnd'],
            ignoreCols=['answerId'],
            idMap=idMap,
            returnUpdates=True):
        if dbEntry:
            # Coins awarded might have been updated afer the fact
            dbEntry.coinsAwarded = dataEntry['coinsAwarded']
        else:
            Session.add(db.Answer(**dataEntry))
            inserts['answer'] += 1
    Session.flush()

    if 'lecture_setting' in data:
        inserts['lecture_setting'] = 0
        for (dataEntry, dbEntry) in findMissingEntries(
                data['lecture_setting'],
                Session.query(db.DeprecatedLectureSetting)
                    .filter(db.DeprecatedLectureSetting.lectureId.in_(idMap['lectureId'].values()))
                    .filter(db.DeprecatedLectureSetting.studentId.in_(idMap['studentId'].values()))
                    .order_by(db.DeprecatedLectureSetting.lectureId, db.DeprecatedLectureSetting.studentId, db.DeprecatedLectureSetting.key),
                sortCols=['lectureId', 'studentId', 'key'],
                idMap=idMap):
            Session.add(db.DeprecatedLectureSetting(**dataEntry))
            inserts['lecture_setting'] += 1
        Session.flush()

    inserts['coin_award'] = 0
    for (dataEntry, dbEntry) in findMissingEntries(
            data['coin_award'],
            Session.query(db.CoinAward)
                .filter(db.CoinAward.studentId.in_(idMap['studentId'].values()))
                .filter(coinAwardFilter)
                .order_by(db.CoinAward.studentId, db.CoinAward.awardTime),
            sortCols=['studentId', 'awardTime'],
            ignoreCols=['coinAwardId'],
            idMap=idMap):
        Session.add(db.CoinAward(**dataEntry))
        inserts['coin_award'] += 1
    Session.flush()

    Session.flush()
    return inserts
示例#3
0
    def asDict(self, data=None):
        """Show coins given to student"""
        student = self.getCurrentStudent()

        (lastAwardTime, walletId, coinClaimed) = (
            Session.query(
                func.max(db.CoinAward.awardTime),
                func.max(
                    db.CoinAward.walletId),  #TODO: Should be last, not max
                func.sum(db.CoinAward.amount),
            ).filter(db.CoinAward.studentId == student.studentId).first())
        if coinClaimed is None:
            lastAwardTime = 0
            coinClaimed = 0
            walletId = ''

        history = []
        coinAwarded = 0
        for row in (Session.query(
                db.Answer.timeEnd, db.Answer.coinsAwarded,
                db.Lecture.plonePath).join(db.Lecture).filter(
                    db.Lecture.hostId == self.getDbHost().hostId).filter(
                        db.Answer.studentId == student.studentId).filter(
                            db.Answer.practice == False).filter(
                                db.Answer.coinsAwarded > 0).order_by(
                                    db.Answer.timeEnd, db.Lecture.plonePath)):

            coinAwarded += row[1]
            history.insert(
                0,
                dict(lecture=row[2],
                     time=calendar.timegm(row[0].timetuple())
                     if row[1] else None,
                     amount=row[1],
                     claimed=(coinAwarded <= coinClaimed
                              and row[0] <= lastAwardTime)))

        # Check if wallet ID is provided, if so pay up.
        txId = None
        if data is not None and data.get('walletId', None):
            walletId = data['walletId']

            # Validate Captcha if not a unittest wallet
            if walletId.startswith('$$UNITTEST'):
                pass
            elif walletId == '$$DONATE:EIAS':
                walletId = EIAS_WALLET
            else:
                remote_addr = self.request.get('HTTP_X_FORWARDED_FOR',
                                               '').split(',')[0]
                if not remote_addr:
                    remote_addr = self.request.get('REMOTE_ADDR')

                if captcha:
                    res = captcha.submit(data.get('captchaResponse', ''),
                                         coin_config.CAPTCHA_KEY, remote_addr)
                    if res.error_code:
                        raise ValueError("Could not validate CAPTCHA")
                    elif not res.is_valid:
                        raise ValueError("Invalid CAPTCHA")

            # Have we already given out our maximum for today?
            dailyTotalAward = (Session.query(func.sum(
                db.CoinAward.amount)).filter(db.CoinAward.awardTime > (
                    utcnow() - datetime.timedelta(days=1))).one())[0] or 0
            if dailyTotalAward > MAX_DAILY_AWARD:
                raise ValueError(
                    "We have distributed all awards available for today")

            # Has this student already got their coins for the hour?
            hourlyStudentTotal = (Session.query(func.sum(
                db.CoinAward.amount)).filter(
                    db.CoinAward.studentId == student.studentId).filter(
                        db.CoinAward.awardTime >
                        (utcnow() -
                         datetime.timedelta(hours=1))).one())[0] or 0
            coinOwed = min(
                coinAwarded - coinClaimed,
                MAX_STUDENT_HOURLY_AWARD - hourlyStudentTotal,
            )
            if coinOwed == 0 and (coinAwarded - coinClaimed) > 0:
                raise ValueError("You cannot redeem any more awards just yet")

            # Perform transaction
            txId = coin.sendTransaction(walletId, coinOwed)

            # Worked, so update database
            Session.add(
                db.CoinAward(
                    studentId=student.studentId,
                    amount=int(coinOwed),
                    walletId=walletId,
                    txId=txId,
                    awardTime=utcnow(),  # NB: So it gets mocked in the tests
                ))
            Session.flush()

            # Worked, so should be even now
            for h in history:
                h['claimed'] = True
            coinClaimed += coinOwed

        return dict(
            walletId=walletId,
            history=history,
            coin_available=int(coinAwarded - coinClaimed),
            tx_id=txId,
        )