예제 #1
0
 def _initialize(self):
     self._initialize_db()
     self.pairingsRepo = AssociationRepository(self.dao,
                                               self.graded_activity)
     self.review_repo = WorkRepositoryLoaderFactory.make(
         course=environment.CONFIG.course,
         activity=self.review_activity,
         rest_timeout=5)
    def setUp(self):
        dao = SqliteDAO()
        self.session = dao.session
        # self.student_ids = [i for i in range(0,5)]
        self.create_new_and_preexisting_students()

        self.unit = unit_factory()
        self.activity = self.unit.initial_work
        self.activity_id = self.activity.id
        self.obj = AssociationRepository(dao, self.activity)
예제 #3
0
    def _initialize(self, unit):
        self.data = []
        self.unit = unit

        # The activity whose id is used to store review pairings for the whole SKAA
        self.activity_for_review_pairings = self.unit.discussion_review
        self.components = [
            c for c in self.unit.components if isinstance(c, DiscussionGroup)
        ]
        self.studentRepo = StudentRepository(environment.CONFIG.course)
        self.studentRepo.download()
        self._initialize_db()
        self.assignRepo = AssociationRepository(
            self.dao, self.activity_for_review_pairings)
예제 #4
0
    def _initialize(self, unit):
        self.data = []

        self.unit = unit
        # The activity whose id is used to store review pairings for the whole SKAA
        self.activity_for_review_pairings = self.unit.initial_work

        self.components = [
            c for c in self.unit.components if isinstance(c, SkaaReviewGroup)
        ]

        self._initialize_db()

        self.assignRepo = AssociationRepository(
            self.dao, self.activity_for_review_pairings)
예제 #5
0
    def _initialize(self):
        """
        Handle instantiating and loading repositories
        :return:
        """
        self.workRepo = WorkRepositoryLoaderFactory.make(
            course=environment.CONFIG.course,
            activity=self.activity,
            rest_timeout=self.wait)

        self.assignment = environment.CONFIG.course.get_assignment(
            self.activity.id)

        self.subRepo = AssignmentSubmissionRepository(self.assignment)
        # shove the activity onto a sub repo so it will resemble
        # a quizrepo for the grader
        self.subRepo.activity = self.activity

        # Filter previously graded
        self.subRepo.data = [
            s for s in self.subRepo.data if s.workflow_state != 'complete'
        ]
        self.workRepo.data = self.workRepo.data[
            self.workRepo.data.workflow_state != 'complete'].copy(deep=True)

        self.workRepo.data.reset_index(inplace=True)

        # We will need the association repo if the activity can be
        # blocked or graded by another student
        self.association_repo = None
        if isinstance(self.activity, BlockableActivity):
            dao = SqliteDAO()
            self.association_repo = AssociationRepository(dao, self.activity)
예제 #6
0
    def _initialize( self ):
        """Creates and populates all relevant repositories and db access points"""
        self.studentRepo = StudentRepository( self.course )
        self.studentRepo.download()
        self._initialize_db()
        self.associationRepo = AssociationRepository( self.dao, self.activity_for_review_pairings)

        self.display_manager = DisplayManager(self.activity)
예제 #7
0
class ReviewBased(IGradingMethod, StoreMixin, DaoMixin):
    def __init__(self,
                 graded_activity: Activity,
                 review_activity: Activity,
                 review_columns: list,
                 pct_of_score=1,
                 **kwargs):
        """

        :param graded_activity: Activity whose grade depends on review
        :param review_activity: Activity where the reviewer gave scores
        :param review_columns: Columns in the review activity to use
        """
        self.pct_of_score = pct_of_score
        self.graded_activity = graded_activity
        self.activity = graded_activity
        self.review_columns = review_columns
        self.review_activity = review_activity

        # todo make this a param or variable...
        self.valmap = {
            'strongly agree': 1,
            'agree': 0.9,
            'disagree': 0.5,
            'strongly disagree': 0.1
        }

        self.handle_kwargs(**kwargs)

        self._initialize()

    def _initialize(self):
        self._initialize_db()
        self.pairingsRepo = AssociationRepository(self.dao,
                                                  self.graded_activity)
        self.review_repo = WorkRepositoryLoaderFactory.make(
            course=environment.CONFIG.course,
            activity=self.review_activity,
            rest_timeout=5)

    def grade(self, student_id):
        # todo assume only one column for now
        col = self.review_columns[0]
        ra = self.pairingsRepo.get_by_author(student_id)
        reviewer_work = self.review_repo.get_student_work(ra.assessor_id)
        v = reviewer_work[col]  #.value

        # reformat so don't blow up if i use different caps
        v = v.strip().lower()

        return self.valmap.get(v)
예제 #8
0
    def _initialize(self):
        """
        Handle instantiating and loading repositories
        :return:
        """
        self.workRepo = WorkRepositoryLoaderFactory.make(
            course=environment.CONFIG.course,
            activity=self.activity,
            rest_timeout=self.wait)

        try:
            self.quiz = environment.CONFIG.course.get_quiz(
                self.activity.quiz_id)
        except AttributeError:
            # todo dev hope this doesn't cause problems!
            self.quiz = environment.CONFIG.course.get_quiz(self.activity.id)

        self.subRepo = QuizSubmissionRepository(self.quiz)

        # If this is a quiz type assignment, we need to get the quiz submission objects
        # not the regular submission object so we can use them for uploading
        self.qsubs = [s for s in self.quiz.get_submissions()]

        # Filter previously graded
        self.subRepo.data = [
            s for s in self.subRepo.data if s.workflow_state != 'complete'
        ]
        self.workRepo.data = self.workRepo.data[
            self.workRepo.data.workflow_state != 'complete'].copy(deep=True)

        self.workRepo.data.reset_index(inplace=True)

        # We will need the association repo if the activity can be
        # blocked or graded by another student
        self.association_repo = None
        if isinstance(self.activity, BlockableActivity):
            dao = SqliteDAO()
            self.association_repo = AssociationRepository(dao, self.activity)
class TestAssociationRepository( TestingBase ):
    def setUp(self):
        dao = SqliteDAO()
        self.session = dao.session
        # self.student_ids = [i for i in range(0,5)]
        self.create_new_and_preexisting_students()

        self.unit = unit_factory()
        self.activity = self.unit.initial_work
        self.activity_id = self.activity.id
        self.obj = AssociationRepository(dao, self.activity)

    def test__make_associations_single_submissions( self ):
        # Students only submit once
        # id case
        assoc = self.obj._make_associations(self.student_ids)
        self.assertEqual(len(self.students), len(assoc) , "single submission; ids")

        # obj case
        assoc = self.obj._make_associations(self.students)
        self.assertEqual(len(self.students), len(assoc) , "single submission; objects" )

    def test__make_associations_3_submissions( self ):
        # Students only submit once
        # id case
        self.student_ids = [i for i in range(0,3)]
        assoc = self.obj._make_associations(self.student_ids)
        self.assertEqual(3, len(assoc) , "single submission; ids")

        # obj case
        self.students = [student_factory() for i in range(0,3)]
        assoc = self.obj._make_associations(self.students)
        self.assertEqual(3, len(assoc) , "single submission; objects" )

    def test__make_associations_double_submissions( self ):
        # Students only submit twice adjacent
        s = []
        for so in self.students:
            s.append(so)
            s.append(so)
        # obj case
        assoc = self.obj._make_associations(s)
        for a, b in assoc:
            self.assertNotEqual(a, b,  "self-unit multi submission; objects"  )

    def test__make_associations_double_submissions_ids( self ):
        s = []
        for so in self.student_ids:
            s.append(so)
            s.append(so)
        # obj case
        assoc = self.obj._make_associations(s)
        for a, b in assoc:
            self.assertNotEqual(a, b,  "self-unit multi submission; objects"  )

    def test_assign_reviewer_raises_when_all_submitters_already_assigned( self ):
        preexisting_pairings = self.create_preexisting_review_pairings(self.activity_id, self.preexisting_students)

        with self.assertRaises(AllAssigned):
            self.obj.assign_reviewers(self.preexisting_students)

    def test_assign_reviewer_raises_when_only_one_submitter( self ):
        with self.assertRaises(NoAvailablePartner):
            self.obj.assign_reviewers(self.preexisting_students[:1])
예제 #10
0
class SkaaOverviewRepository(DaoMixin):
    """
    Holds consolidated information about statuses etc for
    Skaa assignments

    Other classes like dashboard.skaaDashboard are in
    charge of displaying the information
    """
    def __init__(self, unit=None):
        self.unit = unit
        self.studentRepo = StudentRepository(environment.CONFIG.course)
        self.studentRepo.download()

        if unit is not None:
            # Load and display counts
            self.load(unit)

    def _initialize(self, unit):
        self.data = []

        self.unit = unit
        # The activity whose id is used to store review pairings for the whole SKAA
        self.activity_for_review_pairings = self.unit.initial_work

        self.components = [
            c for c in self.unit.components if isinstance(c, SkaaReviewGroup)
        ]

        self._initialize_db()

        self.assignRepo = AssociationRepository(
            self.dao, self.activity_for_review_pairings)

    def load(self, unit):
        """
        Main called method which initializes and loads data
        Often will have to be called later than initialization
        so that we can have the object existing and then specify
        which unit to load.

        :param unit:
        :return:
        """
        self._initialize(unit)

        for sid, obj in self.studentRepo.data.items():
            d = {
                'student': obj.name,
                'canvas_id': sid,
                'csun_id': obj.sis_user_id
            }
            for c in self.components:
                if len(self.assignRepo.get_associations()) > 0:
                    try:
                        # get the record where the student is the reviwer
                        a = self.assignRepo.get_by_reviewer(sid)
                        # get the name of the student being assessed
                        d['reviewing'] = self.studentRepo.get_student_name(
                            a.assessee_id)
                        d['reviewing_id'] = a.assessee_id
                        # get the record where the student is the author
                        b = self.assignRepo.get_by_author(sid)
                        # get the name
                        d['reviewed_by'] = self.studentRepo.get_student_name(
                            b.assessor_id)
                        d['reviewed_by_id'] = b.assessor_id
                    except AttributeError:
                        pass

                self.add_invites(d, c, sid)

                self.add_reviews(d, c, sid)

            self.data.append(d)

        self.data = pd.DataFrame(self.data)

    def add_invites(self, data_dict, component, student_id):
        invite_fields = {
            Review: 'invited_to_review',
            MetaReview: 'invited_to_metareview',
            DiscussionReview: 'invited_to_discussion_review'
        }

        invite_fieldname = invite_fields.get(type(component))

        if invite_fieldname is not None:
            inv = InvitationStatusRepository(self.dao, component)
            data_dict[invite_fieldname] = pd.to_datetime(
                inv.received_at(student_id))

    def add_reviews(self, data_dict, component, student_id):
        # Note: can't do in similar way to invitations since invited to metareview and received ca feedback
        # use different activities. The invitation is for the upcoming one which provides feedback

        # on the previous one
        # set to none so won't overwrite on next time through
        fb_fieldname = None

        if isinstance(component, InitialWork):
            # we can't use the review object because feedback on the review
            # comes from the metareview
            fb_fieldname = 'received_feedback_on_essay'

        if isinstance(component, Review):
            fb_fieldname = 'received_feedback_on_review'

        if isinstance(component, DiscussionForum):
            fb_fieldname = 'received_discussion_feedback'

        if fb_fieldname is not None:
            fr = FeedbackStatusRepository(self.dao, component)
            data_dict[fb_fieldname] = pd.to_datetime(
                fr.received_at(student_id))

    @property
    def essay(self):
        """
        Return students who have done initial work and been assigned a reviewer

        :return: DataFrame
        """
        return self.data[~self.data.reviewing.isnull()]

    @property
    def no_essay(self):
        """
        Students who have not submitted the initial work
        :return: DataFrame
        """
        return self.data[self.data.reviewing.isnull()]

    @property
    def reviewed(self):
        """
        Returns the subset of students who have turned in the initial work
        whose reviewer has turned in the review

        :return: DataFrame
        """
        # Students whose reviewer has and has not turned in review
        return self.essay[~self.essay.received_feedback_on_essay.isnull()]

    @property
    def non_reviewed(self):
        """
        Returns the subset of students who have turned in the initial work
        whose reviewer has NOT turned in the review

        :return: DataFrame
        """
        return self.essay[ self.essay.received_feedback_on_essay.isnull() ]\
            # .drop( [ 'reviewing' ], axis=1 )

    @property
    def metareviewed(self):
        """
        Returns the subset of students who have turned in the initial work
        whose author has turned in the metareview

        :return: DataFrame
        """
        return self.essay[~self.essay.received_feedback_on_review.isnull()]

    @property
    def non_metareviewed(self):
        """
        Returns the subset of students who have turned in the initial work
        whose author has turned in the metareview

        :return: DataFrame
        """
        return self.essay[self.essay.received_feedback_on_review.isnull()]
예제 #11
0
class DiscussionOverviewRepository(DaoMixin):
    """
    Holds information about the discussion and discussion
    review for a unit

    Other classes like dashboard.discussionDashboard are in
    charge of displaying the information
    """
    def __init__(self, unit=None):
        """
        Initializes and loads all data or
        waits to have load called

        :return:
        """
        self.unit = unit
        self.studentRepo = StudentRepository(environment.CONFIG.course)
        self.studentRepo.download()

        if unit is not None:
            # Load and display counts
            self.load(unit)

    def _initialize(self, unit):
        self.data = []
        self.unit = unit

        # The activity whose id is used to store review pairings for the whole SKAA
        self.activity_for_review_pairings = self.unit.discussion_review
        self.components = [
            c for c in self.unit.components if isinstance(c, DiscussionGroup)
        ]
        self.studentRepo = StudentRepository(environment.CONFIG.course)
        self.studentRepo.download()
        self._initialize_db()
        self.assignRepo = AssociationRepository(
            self.dao, self.activity_for_review_pairings)

    def load(self, unit):

        self._initialize(unit)

        for sid, obj in self.studentRepo.data.items():
            d = {
                'student': obj.name,
                'canvas_id': sid,
                'csun_id': obj.sis_user_id
            }
            for c in self.components:
                if len(self.assignRepo.get_associations()) > 0:
                    try:
                        # get the record where the student is the reviwer
                        a = self.assignRepo.get_by_reviewer(sid)
                        # get the name of the student being assessed
                        d['reviewing'] = self.studentRepo.get_student_name(
                            a.assessee_id)
                        d['reviewing_id'] = a.assessee_id
                        # get the record where the student is the author
                        b = self.assignRepo.get_by_author(sid)
                        # get the name
                        d['reviewed_by'] = self.studentRepo.get_student_name(
                            b.assessor_id)
                        d['reviewed_by_id'] = b.assessor_id
                    except AttributeError:
                        pass

                self.add_invites(d, c, sid)

                self.add_reviews(d, c, sid)

            self.data.append(d)

        self.data = pd.DataFrame(self.data)

    def add_invites(self, data_dict, component, student_id):
        invite_fields = {DiscussionReview: 'invited_to_discussion_review'}

        invite_fieldname = invite_fields.get(type(component))

        if invite_fieldname is not None:
            inv = InvitationStatusRepository(self.dao, component)
            data_dict[invite_fieldname] = pd.to_datetime(
                inv.received_at(student_id))

    def add_reviews(self, data_dict, component, student_id):
        # Note: can't do in similar way to invitations since invited to metareview and received ca feedback
        # use different activities. The invitation is for the upcoming one which provides feedback
        # on the previous one

        # set to none so won't overwrite on next time through
        fb_fieldname = None

        if isinstance(component, DiscussionForum):
            fb_fieldname = 'received_discussion_feedback'

        if fb_fieldname is not None:
            fr = FeedbackStatusRepository(self.dao, component)
            data_dict[fb_fieldname] = pd.to_datetime(
                fr.received_at(student_id))

    @property
    def posters(self):
        """
        Students who have posted and been assigned reviewers
        todo Consider whether should be using the assignment of reviewers or a status object
        :return: DataFrame
        """
        return self.data[~self.data.reviewing.isnull()]

    @property
    def non_posters(self):
        """
        Students who have not posted and thus not been assigned a reviewer
        :return: DataFrame
        """
        return self.data[self.data.reviewing.isnull()]

    @property
    def reviewed(self):
        """
        Students whose reviewer has turned in the review
        :return: DataFrame
        """
        return self.posters[~self.posters.received_discussion_feedback.isnull(
        )]

    @property
    def non_reviewed(self):
        """
        Students whose reviewer has NOT turned in the review
        :return: DataFrame
        """
        return self.posters[self.posters.received_discussion_feedback.isnull()]

    def add_invites(self, data_dict, component, student_id):
        invite_fields = {DiscussionReview: 'invited_to_discussion_review'}

        invite_fieldname = invite_fields.get(type(component))

        if invite_fieldname is not None:
            inv = InvitationStatusRepository(self.dao, component)
            data_dict[invite_fieldname] = pd.to_datetime(
                inv.received_at(student_id))

    def add_reviews(self, data_dict, component, student_id):
        # Note: can't do in similar way to invitations since invited to metareview and received ca feedback
        # use different activities. The invitation is for the upcoming one which provides feedback
        # on the previous one

        # set to none so won't overwrite on next time through
        fb_fieldname = None

        # todo this probably should be changed to discussion forum everywhere
        if isinstance(component, DiscussionReview):
            fb_fieldname = 'received_discussion_feedback'

        if fb_fieldname is not None:
            fr = FeedbackStatusRepository(self.dao, component)
            data_dict[fb_fieldname] = pd.to_datetime(
                fr.received_at(student_id))