예제 #1
0
def produceTest(self, t):
    """Someone has told us they produced (made PDF) for this paper.

    Args:
        t (int): a paper number.

    Exceptions:
        IndexError: no such paper exists.
        ValueError: you already told us you made it.
    """
    tref = Test.get_or_none(test_number=t)
    if tref is None:
        log.error(
            'Cannot set paper {} to "produced" - it does not exist'.format(t))
        raise IndexError("Paper number does not exist: out of range?")
    else:
        # TODO - work out how to make this more efficient? Multiple updates in one op?
        with plomdb.atomic():
            if tref.produced:
                # TODO be less harsh if we have the same md5sum
                log.error('Paper {} was already "produced"!'.format(t))
                raise ValueError("Paper was already produced")
            tref.produced = True
            tref.save()
        log.info('Paper {} is set to "produced"'.format(t))
예제 #2
0
def createDNMGroup(self, t, pages):
    tref = Test.get_or_none(test_number=t)
    if tref is None:
        log.warning("Create DNM - No test with number {}".format(t))
        return False

    gid = "d{}".format(str(t).zfill(4))
    with plomdb.atomic():
        # make the dnmgroup
        try:
            # A DNM group may have 0 pages, in that case mark it as scanned and set status = "complete"
            sc = True if len(pages) == 0 else False
            gref = Group.create(
                test=tref,
                gid=gid,
                group_type="d",
                scanned=sc,
                queue_position=self.nextqueue_position(),
            )
        except pw.IntegrityError as e:
            log.error(
                "Create DNM - cannot make Group {} of Test {} error - {}".
                format(gid, t, e))
            return False
        try:
            dref = DNMGroup.create(test=tref, group=gref)
        except pw.IntegrityError as e:
            log.error(
                "Create DNM - cannot create DNMGroup {} of group {} error - {}."
                .format(dref, gref, e))
            return False
        return self.addTPages(tref, gref, t, pages, 1)
예제 #3
0
def createIDGroup(self, t, pages):
    tref = Test.get_or_none(test_number=t)
    if tref is None:
        log.warning("Create IDGroup - No test with number {}".format(t))
        return False
    with plomdb.atomic():
        # make the Group
        gid = "i{}".format(str(t).zfill(4))
        try:
            gref = Group.create(
                test=tref,
                gid=gid,
                group_type="i",
                queue_position=self.nextqueue_position(),
            )  # must be unique
        except pw.IntegrityError as e:
            log.error("Create ID for gid={} test={}: cannot create Group - {}".
                      format(gid, t, e))
            return False
        # make the IDGroup
        try:
            iref = IDGroup.create(test=tref, group=gref)
        except pw.IntegrityError as e:
            log.error(
                "Create ID for gid={} test={} Group={}: cannot create IDGroup - {}."
                .format(gid, t, gref, e))
            return False
        return self.addTPages(tref, gref, t, pages, 1)  # always version 1.
예제 #4
0
def createTest(self, t):
    with plomdb.atomic():
        try:
            tref = Test.create(test_number=t)  # must be unique
        except pw.IntegrityError as e:
            log.error("Create test {} error - {}".format(t, e))
            return False
    return True
예제 #5
0
def id_paper(self, paper_num, user_name, sid, sname):
    """Associate student name and id with a paper in the database.

    See also :func:`plom.db.db_identify.ID_id_paper` which is similar.

    Args:
        paper_num (int)
        user_name (str): User who did the IDing.
        sid (str): student id.
        sname (str): student name.

    Returns:
        tuple: `(True, None, None)` if succesful, `(False, 409, msg)`
            means `sid` is in use elsewhere, a serious problem for
            the caller to deal with.  `(False, int, msg)` covers all
            other errors.  `msg` gives details about errors.  Some
            of these should not occur, and indicate possible bugs.
            `int` gives a hint of suggested HTTP status code,
            currently it can be 404 or 409.

    TODO: perhaps several sorts of exceptions would be better.
    """
    uref = User.get(name=user_name)  # TODO: or hardcode HAL like before
    # since user authenticated, this will always return legit ref.

    logbase = 'User "{}" tried to ID paper {}'.format(user_name, paper_num)
    with plomdb.atomic():
        tref = Test.get_or_none(Test.test_number == paper_num)
        if tref is None:
            msg = "denied b/c paper not found"
            log.error("{}: {}".format(logbase, msg))
            return False, 404, msg
        iref = tref.idgroups[0]
        iref.user = uref
        iref.status = "done"
        iref.student_id = sid
        iref.student_name = sname
        iref.identified = True
        iref.time = datetime.now()
        try:
            iref.save()
        except pw.IntegrityError:
            msg = "student id {} already entered elsewhere".format(
                censorID(sid))
            log.error("{} but {}".format(logbase, msg))
            return False, 409, msg
        tref.identified = True
        tref.save()
        log.info('Paper {} ID\'d by "{}" as "{}" "{}"'.format(
            paper_num, user_name, censorID(sid), censorName(sname)))
    return True, None, None
예제 #6
0
def MmodifyRubric(self, user_name, key, change):
    """Modify or create a rubric based on an existing rubric in the DB.

    Currently this modifies the existing rubric, increasing its revision
    number.  However, this is subject to change and should be considered
    an implementation detail.  Its very likely we will move to an
    immutable model.  At any rate, the returned `new_key` should be
    considered as replacing the original and the old key should not be
    used to place new annotations.  It might however be used to find
    outdated ones to tag or otherwise update papers.

    Args:
        user_name (str): name of user creating the rubric element
        key(str): key for the rubric
        change (dict): dict containing the changes to make to the
            rubric.  Must contain these fields:
            `{kind: "relative", delta: "-1", text: "blah", tags: "blah", meta: "blah"}`
            Other fields will be ignored.  Note this means you can think
            you are changing, e.g., the question but this will silently
            not happen.
            TODO: in the future we might prevent changing the "kind"
            or the sign of the delta.

    Returns:
        tuple: `(True, new_key)` containing the newly generated key
             (which might be the old key but this is not promised),
             or `(False, "incomplete")`, or `(False, "noSuchRubric")`.
    """
    need_fields = ("delta", "text", "tags", "meta", "kind")
    if any(x not in change for x in need_fields):
        return (False, "incomplete")
    uref = User.get(name=user_name)  # authenticated, so not-None
    # check if the rubric exists made by this user - cannot modify other user's rubric
    # TODO: should we have another bail case here `(False, "notYours")`?
    # TODO: maybe manager will be able modify all rubrics.
    rref = Rubric.get_or_none(key=key, user=uref)
    if rref is None:
        return (False, "noSuchRubric")

    with plomdb.atomic():
        rref.kind = change["kind"]
        rref.delta = change["delta"]
        rref.text = change["text"]
        rref.modificationTime = datetime.now()
        rref.revision += 1
        rref.meta = change["meta"]
        rref.tags = change["tags"]
        rref.save()
    return (True, key)
예제 #7
0
def McreateRubric(self, user_name, rubric):
    """Create a new rubric entry in the DB

    Args:
        user_name (str): name of user creating the rubric element
        rubric (dict): dict containing the rubric details.
            Must contain these fields:
            `{kind: "relative", delta: "-1", text: "blah", question: 2}`
            The following fields are optional and empty strings will be
            substituted:
            `{tags: "blah", meta: "blah"}`
            Currently, its ok if it contains other fields: they are
            ignored.

    Returns:
        tuple: `(True, key)` or `(False, err_msg)` where `key` is the
            key for the new rubric.  Can fail if missing fields.
    """
    need_fields = ("kind", "delta", "text", "question")
    optional_fields = ("tags", "meta")
    if any(x not in rubric for x in need_fields):
        return (False, "Must have all fields {}".format(need_field))
    for f in optional_fields:
        if f not in rubric:
            rubric = rubric.copy()  # in case caller uses reference
            rubric[f] = ""
    uref = User.get(name=user_name)  # authenticated, so not-None
    with plomdb.atomic():
        # build unique key while holding atomic access
        key = generate_new_comment_ID()
        while Rubric.get_or_none(key=key) is not None:
            key = generate_new_comment_ID()
        Rubric.create(
            key=key,
            user=uref,
            question=rubric["question"],
            kind=rubric["kind"],
            delta=rubric["delta"],
            text=rubric["text"],
            creationTime=datetime.now(),
            modificationTime=datetime.now(),
            meta=rubric["meta"],
            tags=rubric["tags"],
        )
    return (True, key)
예제 #8
0
def createQGroup(self, t, q, v, pages):
    tref = Test.get_or_none(test_number=t)
    if tref is None:
        log.warning("Create Q - No test with number {}".format(t))
        return False

    gid = "q{}g{}".format(str(t).zfill(4), q)

    with plomdb.atomic():
        # make the qgroup
        try:
            gref = Group.create(
                test=tref,
                gid=gid,
                group_type="q",
                version=v,
                queue_position=self.nextqueue_position(),
            )
        except pw.IntegrityError as e:
            log.error(
                "Create Q - cannot create group {} of Test {} error - {}".
                format(gid, t, e))
            return False
        try:
            # qref = QGroup.create(
            #     test=tref, group=gref, question=q, version=v, fullmark=mark
            # )
            qref = QGroup.create(test=tref, group=gref, question=q, version=v)
        except pw.IntegrityError as e:
            log.error(
                "Create Q - cannot create QGroup of question {} error - {}.".
                format(gid, e))
            return False
        ## create annotation 0 owned by HAL
        try:
            uref = User.get(name="HAL")
            aref = Annotation.create(qgroup=qref, edition=0, user=uref)
        except pw.IntegrityError as e:
            log.error(
                "Create Q - cannot create Annotation  of question {} error - {}."
                .format(gid, e))
            return False

        return self.addTPages(tref, gref, t, pages, v)
예제 #9
0
def addTPages(self, tref, gref, t, pages, v):
    """
    For initial construction of test-pages for a test. We use these so we know what structured pages we should have.
    """
    flag = True
    with plomdb.atomic():
        for p in pages:
            try:
                TPage.create(
                    test=tref,
                    group=gref,
                    page_number=p,
                    version=v,
                    scanned=False,
                )
            except pw.IntegrityError as e:
                log.error("Adding page {} for test {} error - {}".format(
                    p, t, e))
                flag = False
    return flag