Exemple #1
0
    def generate_or_link_user_account(self):
        from . import SystemRole, User

        if not self.user:
            # check if global_unique_identifier user already exists
            if self.global_unique_identifier:
                self.user = User.query \
                    .filter_by(global_unique_identifier=self.global_unique_identifier) \
                    .one_or_none()

            if not self.user:
                self.user = User(
                    username=None,
                    password=None,
                    system_role=self._get_system_role(),
                    global_unique_identifier=self.global_unique_identifier
                )
                self._sync_name()
                self._sync_email()
                if self.user.system_role == SystemRole.student:
                    self._sync_student_number()

                # instructors can have their display names set to their full name by default
                if self.user.system_role != SystemRole.student and self.user.fullname != None:
                    self.user.displayname = self.user.fullname
                else:
                    self.user.displayname = display_name_generator(self.user.system_role.value)
Exemple #2
0
    def generate_or_link_user_account(self):
        from . import SystemRole, User

        if self.compair_user_id == None and self.global_unique_identifier:
            self.compair_user = User.query \
                .filter_by(global_unique_identifier=self.global_unique_identifier) \
                .one_or_none()

            if not self.compair_user:
                self.compair_user = User(
                    username=None,
                    password=None,
                    system_role=self.system_role,
                    firstname=self.lis_person_name_given,
                    lastname=self.lis_person_name_family,
                    email=self.lis_person_contact_email_primary,
                    global_unique_identifier=self.global_unique_identifier
                )
                if self.compair_user.system_role == SystemRole.student:
                    self.compair_user.student_number = self.student_number

                # instructors can have their display names set to their full name by default
                if self.compair_user.system_role != SystemRole.student and self.compair_user.fullname != None:
                    self.compair_user.displayname = self.compair_user.fullname
                else:
                    self.compair_user.displayname = display_name_generator(self.compair_user.system_role.value)

            db.session.commit()
Exemple #3
0
    def generate_or_link_user_account(self):
        from . import SystemRole, User

        if self.compair_user_id == None and self.global_unique_identifier:
            self.compair_user = User.query \
                .filter_by(global_unique_identifier=self.global_unique_identifier) \
                .one_or_none()

            if not self.compair_user:
                self.compair_user = User(
                    username=None,
                    password=None,
                    system_role=self.system_role,
                    firstname=self.lis_person_name_given,
                    lastname=self.lis_person_name_family,
                    email=self.lis_person_contact_email_primary,
                    global_unique_identifier=self.global_unique_identifier
                )
                if self.compair_user.system_role == SystemRole.student:
                    self.compair_user.student_number = self.student_number

                # instructors can have their display names set to their full name by default
                if self.compair_user.system_role != SystemRole.student and self.compair_user.fullname != None:
                    self.compair_user.displayname = self.compair_user.fullname
                else:
                    self.compair_user.displayname = display_name_generator(self.compair_user.system_role.value)

            db.session.commit()
Exemple #4
0
    def get(self):
        """
        Returns information related to the current user:
        any linked course, assignment, etc. Helps inform
        the app as to what to do next, for a given state.
        """

        if not sess.get('LTI'):
            return {"status": {'valid': False}}

        lti_resource_link = LTIResourceLink.query.get(
            sess.get('lti_resource_link'))
        lti_context = LTIContext.query.get(
            sess.get('lti_context')) if sess.get('lti_context') else None
        lti_user_resource_link = LTIUserResourceLink.query.get(
            sess.get('lti_user_resource_link'))
        lti_user = LTIUser.query.get(sess.get('lti_user'))
        status = {
            'valid': True,
            'assignment': {
                'id':
                lti_resource_link.compair_assignment_uuid
                if lti_resource_link.compair_assignment_id != None else None,
                'exists':
                lti_resource_link.compair_assignment_id != None
            },
            'course': {
                'name':
                lti_context.context_title if lti_context else None,
                'id':
                lti_context.compair_course_uuid
                if lti_context and lti_context.compair_course_id else None,
                'exists':
                lti_context and lti_context.compair_course_id != None,
                'course_role':
                lti_user_resource_link.course_role.value
                if lti_user_resource_link else None
            },
            'user': {
                'exists': lti_user.compair_user_id != None,
                'firstname': lti_user.lis_person_name_given,
                'lastname': lti_user.lis_person_name_family,
                'student_number': lti_user.student_number,
                'displayname':
                display_name_generator(lti_user.system_role.value),
                'email': lti_user.lis_person_contact_email_primary,
                'system_role': lti_user.system_role.value
            }
        }

        return {"status": status}
Exemple #5
0
    def get(self):
        """
        Returns information related to the current user:
        any linked course, assignment, etc. Helps inform
        the app as to what to do next, for a given state.
        """

        if not sess.get('LTI'):
            return { "status" : { 'valid': False } }

        lti_resource_link = LTIResourceLink.query.get(sess.get('lti_resource_link'))
        lti_context = LTIContext.query.get(sess.get('lti_context')) if sess.get('lti_context') else None
        lti_user_resource_link = LTIUserResourceLink.query.get(sess.get('lti_user_resource_link'))
        lti_user = LTIUser.query.get(sess.get('lti_user'))
        status = {
            'valid' : True,
            'assignment': {
                'id': lti_resource_link.compair_assignment_uuid if lti_resource_link.compair_assignment_id != None else None,
                'exists': lti_resource_link.compair_assignment_id != None
            },
            'course': {
                'name': lti_context.context_title if lti_context else None,
                'id': lti_context.compair_course_uuid if lti_context and lti_context.compair_course_id else None,
                'exists': lti_context and lti_context.compair_course_id != None,
                'course_role': lti_user_resource_link.course_role.value if lti_user_resource_link else None
            },
            'user': {
                'exists': lti_user.compair_user_id != None,
                'firstname': lti_user.lis_person_name_given,
                'lastname': lti_user.lis_person_name_family,
                'student_number': lti_user.student_number,
                'displayname': display_name_generator(lti_user.system_role.value),
                'email': lti_user.lis_person_contact_email_primary,
                'system_role': lti_user.system_role.value
            }
        }

        return { "status" : status }
Exemple #6
0
    def post(self):
        if not current_app.config.get('DEMO_INSTALLATION', False):
            abort(404, title="Demo Accounts Unavailable", message="Sorry, the system settings do now allow the use of demo accounts.")

        params = new_user_demo_parser.parse_args()

        user = User()
        user.password = "******"

        system_role = params.get("system_role")
        check_valid_system_role(system_role)
        user.system_role = SystemRole(system_role)

        user_count = User.query \
            .filter_by(system_role=user.system_role) \
            .count()
        user_count += 1

        # username
        while True:
            if user.system_role == SystemRole.sys_admin:
                user.username="******"+str(user_count)
            elif user.system_role == SystemRole.instructor:
                user.username="******"+str(user_count)
            else:
                user.username="******"+str(user_count)

            username_exists = User.query.filter_by(username=user.username).first()
            if not username_exists:
                break
            else:
                user_count+=1

        if user.system_role == SystemRole.sys_admin:
            user.firstname = "Admin"
            user.lastname = str(user_count)
            user.displayname = "Admin "+str(user_count)
        elif user.system_role == SystemRole.instructor:
            user.firstname = "Instructor"
            user.lastname = str(user_count)
            user.displayname = "Instructor "+str(user_count)

            # create new enrollment
            new_user_course = UserCourse(
                user=user,
                course_id=1,
                course_role=CourseRole.instructor
            )
            db.session.add(new_user_course)
        else:
            user.firstname = "Student"
            user.lastname = str(user_count)
            user.displayname = display_name_generator()

            while True:
                user.student_number = random_generator(8, string.digits)
                student_number_exists = User.query.filter_by(student_number=user.student_number).first()
                if not student_number_exists:
                    break

            # create new enrollment
            new_user_course = UserCourse(
                user=user,
                course_id=1,
                course_role=CourseRole.student
            )
            db.session.add(new_user_course)

        try:
            db.session.add(user)
            db.session.commit()
            on_user_demo_create.send(
                self,
                event_name=on_user_demo_create.name,
                user=current_user,
                data=marshal(user, dataformat.get_full_user()))

        except exc.IntegrityError:
            db.session.rollback()
            current_app.logger.error("Failed to add new user. Duplicate.")
            return {'error': 'A user with the same identifier already exists.'}, 400

        authenticate(user, login_method="Demo")

        return marshal(user, dataformat.get_full_user())
Exemple #7
0
def import_users(import_type, course, users):
    invalids = []  # invalid entries - eg. invalid # of columns
    count = 0  # store number of successful enrolments

    imported_users = []
    set_user_passwords = []

    # store unique user identifiers - eg. student number - throws error if duplicate in file
    import_usernames = []
    import_student_numbers = []

    # store unique user identifiers - eg. student number - throws error if duplicate in file
    existing_system_usernames = _get_existing_users_by_identifier(
        import_type, users)
    existing_system_student_numbers = _get_existing_users_by_student_number(
        import_type, users)

    groups = course.groups.all()
    groups_by_name = {}
    for group in groups:
        groups_by_name[group.name] = group

    # create / update users in file
    for user_row in users:
        if len(user_row) < 1:
            continue  # skip empty row
        user = _parse_user_row(import_type, user_row)

        # validate unique identifier
        username = user.get('username')
        password = user.get(
            'password'
        )  #always None for CAS/SAML import, can be None for existing users on ComPAIR import
        student_number = user.get('student_number')

        u = existing_system_usernames.get(username, None)

        if not username:
            invalids.append({
                'user': User(username=username),
                'message': 'The username is required.'
            })
            continue
        elif username in import_usernames:
            invalids.append({
                'user':
                User(username=username),
                'message':
                'This username already exists in the file.'
            })
            continue

        if u:
            # overwrite password if user has not logged in yet
            if u.last_online == None and not password in [None, '*']:
                set_user_passwords.append((u, password))
        else:
            u = User(username=None,
                     password=None,
                     student_number=user.get('student_number'),
                     firstname=user.get('firstname'),
                     lastname=user.get('lastname'),
                     email=user.get('email'))
            if import_type == ThirdPartyType.cas.value or import_type == ThirdPartyType.saml.value:
                # CAS/SAML login
                u.third_party_auths.append(
                    ThirdPartyUser(
                        unique_identifier=username,
                        third_party_type=ThirdPartyType(import_type)))
            else:
                # ComPAIR login
                u.username = username
                if password in [None, '*']:
                    invalids.append({
                        'user': u,
                        'message': 'The password is required.'
                    })
                    continue
                elif len(password) < 4:
                    invalids.append({
                        'user':
                        u,
                        'message':
                        'The password must be at least 4 characters long.'
                    })
                    continue
                else:
                    set_user_passwords.append((u, password))

            # validate student number (if not None)
            if student_number:
                # invalid if already showed up in file
                if student_number in import_student_numbers:
                    u.username = username
                    invalids.append({
                        'user':
                        u,
                        'message':
                        'This student number already exists in the file.'
                    })
                    continue
                # invalid if student number already exists in the system
                elif student_number in existing_system_student_numbers:
                    u.username = username
                    invalids.append({
                        'user':
                        u,
                        'message':
                        'This student number already exists in the system.'
                    })
                    continue

            u.system_role = SystemRole.student
            u.displayname = user.get('displayname') if user.get(
                'displayname') else display_name_generator()
            db.session.add(u)

        import_usernames.append(username)
        if student_number:
            import_student_numbers.append(student_number)
        imported_users.append((u, user.get('group')))
    db.session.commit()

    enroled = UserCourse.query \
        .filter_by(course_id=course.id) \
        .all()

    enroled = {e.user_id: e for e in enroled}

    students = UserCourse.query \
        .filter_by(
            course_id=course.id,
            course_role=CourseRole.student
        ) \
        .all()
    students = {s.user_id: s for s in students}

    # enrol valid users in file
    for user, group_name in imported_users:
        enrol = enroled.get(user.id,
                            UserCourse(course_id=course.id, user_id=user.id))
        enrol.group = None
        if group_name:
            group = groups_by_name.get(group_name)
            # add new groups if needed
            if not group:
                group = Group(course=course, name=group_name)
                groups_by_name[group_name] = group
                db.session.add(group)
            enrol.group = group

        # do not overwrite instructor or teaching assistant roles
        if enrol.course_role not in [
                CourseRole.instructor, CourseRole.teaching_assistant
        ]:
            enrol.course_role = CourseRole.student
            if user.id in students:
                del students[user.id]
            count += 1
        db.session.add(enrol)

    db.session.commit()

    # unenrol users not in file anymore
    for user_id in students:
        enrolment = students.get(user_id)
        # skip users that are already dropped
        if enrolment.course_role == CourseRole.dropped:
            continue
        enrolment.course_role = CourseRole.dropped
        enrolment.group_id = None
        db.session.add(enrolment)
    db.session.commit()

    # wait until user ids are generated before starting background jobs
    # perform password update in chunks of 100
    chunk_size = 100
    chunks = [
        set_user_passwords[index:index + chunk_size]
        for index in range(0, len(set_user_passwords), chunk_size)
    ]
    for chunk in chunks:
        set_passwords.delay({user.id: password for (user, password) in chunk})

    on_classlist_upload.send(current_app._get_current_object(),
                             event_name=on_classlist_upload.name,
                             user=current_user,
                             course_id=course.id)

    return {
        'success': count,
        'invalids': marshal(invalids,
                            dataformat.get_import_users_results(False))
    }
Exemple #8
0
    def post(self):
        if not current_app.config.get('DEMO_INSTALLATION', False):
            abort(
                404,
                title="Demo Accounts Unavailable",
                message=
                "Sorry, the system settings do now allow the use of demo accounts."
            )

        params = new_user_demo_parser.parse_args()

        user = User()
        user.password = "******"

        system_role = params.get("system_role")
        check_valid_system_role(system_role)
        user.system_role = SystemRole(system_role)

        user_count = User.query \
            .filter_by(system_role=user.system_role) \
            .count()
        user_count += 1

        # username
        while True:
            if user.system_role == SystemRole.sys_admin:
                user.username = "******" + str(user_count)
            elif user.system_role == SystemRole.instructor:
                user.username = "******" + str(user_count)
            else:
                user.username = "******" + str(user_count)

            username_exists = User.query.filter_by(
                username=user.username).first()
            if not username_exists:
                break
            else:
                user_count += 1

        if user.system_role == SystemRole.sys_admin:
            user.firstname = "Admin"
            user.lastname = str(user_count)
            user.displayname = "Admin " + str(user_count)
        elif user.system_role == SystemRole.instructor:
            user.firstname = "Instructor"
            user.lastname = str(user_count)
            user.displayname = "Instructor " + str(user_count)

            # create new enrollment
            new_user_course = UserCourse(user=user,
                                         course_id=1,
                                         course_role=CourseRole.instructor)
            db.session.add(new_user_course)
        else:
            user.firstname = "Student"
            user.lastname = str(user_count)
            user.displayname = display_name_generator()

            while True:
                user.student_number = random_generator(8, string.digits)
                student_number_exists = User.query.filter_by(
                    student_number=user.student_number).first()
                if not student_number_exists:
                    break

            # create new enrollment
            new_user_course = UserCourse(user=user,
                                         course_id=1,
                                         course_role=CourseRole.student)
            db.session.add(new_user_course)

        try:
            db.session.add(user)
            db.session.commit()
            on_user_demo_create.send(self,
                                     event_name=on_user_demo_create.name,
                                     user=current_user,
                                     data=marshal(user,
                                                  dataformat.get_full_user()))

        except exc.IntegrityError:
            db.session.rollback()
            current_app.logger.error("Failed to add new user. Duplicate.")
            return {
                'error': 'A user with the same identifier already exists.'
            }, 400

        authenticate(user, login_method="Demo")

        return marshal(user, dataformat.get_full_user())
Exemple #9
0
def import_users(import_type, course, users):
    invalids = []  # invalid entries - eg. invalid # of columns
    count = 0  # store number of successful enrolments

    imported_users = []
    set_user_passwords = []

    # store unique user identifiers - eg. student number - throws error if duplicate in file
    import_usernames = []
    import_student_numbers = []

    # store unique user identifiers - eg. student number - throws error if duplicate in file
    existing_system_usernames = _get_existing_users_by_identifier(import_type, users)
    existing_system_student_numbers = _get_existing_users_by_student_number(import_type, users)

    groups = course.groups.all()
    groups_by_name = {}
    for group in groups:
        groups_by_name[group.name] = group

    # create / update users in file
    for user_row in users:
        if len(user_row) < 1:
            continue  # skip empty row
        user = _parse_user_row(import_type, user_row)

        # validate unique identifier
        username = user.get('username')
        password = user.get('password') #always None for CAS/SAML import, can be None for existing users on ComPAIR import
        student_number = user.get('student_number')

        u = existing_system_usernames.get(username, None)

        if not username:
            invalids.append({'user': User(username=username), 'message': 'The username is required.'})
            continue
        elif username in import_usernames:
            invalids.append({'user': User(username=username), 'message': 'This username already exists in the file.'})
            continue

        if u:
            # overwrite password if user has not logged in yet
            if u.last_online == None and not password in [None, '*']:
                set_user_passwords.append((u, password))
        else:
            u = User(
                username=None,
                password=None,
                student_number=user.get('student_number'),
                firstname=user.get('firstname'),
                lastname=user.get('lastname'),
                email=user.get('email')
            )
            if import_type == ThirdPartyType.cas.value or import_type == ThirdPartyType.saml.value:
                # CAS/SAML login
                u.third_party_auths.append(ThirdPartyUser(
                    unique_identifier=username,
                    third_party_type=ThirdPartyType(import_type)
                ))
            else:
                # ComPAIR login
                u.username = username
                if password in [None, '*']:
                    invalids.append({'user': u, 'message': 'The password is required.'})
                    continue
                elif len(password) < 4:
                    invalids.append({'user': u, 'message': 'The password must be at least 4 characters long.'})
                    continue
                else:
                    set_user_passwords.append((u, password))

            # validate student number (if not None)
            if student_number:
                # invalid if already showed up in file
                if student_number in import_student_numbers:
                    u.username = username
                    invalids.append({'user': u, 'message': 'This student number already exists in the file.'})
                    continue
                # invalid if student number already exists in the system
                elif student_number in existing_system_student_numbers:
                    u.username = username
                    invalids.append({'user': u, 'message': 'This student number already exists in the system.'})
                    continue

            u.system_role = SystemRole.student
            u.displayname = user.get('displayname') if user.get('displayname') else display_name_generator()
            db.session.add(u)

        import_usernames.append(username)
        if student_number:
            import_student_numbers.append(student_number)
        imported_users.append( (u, user.get('group')) )
    db.session.commit()

    enroled = UserCourse.query \
        .filter_by(course_id=course.id) \
        .all()

    enroled = {e.user_id: e for e in enroled}

    students = UserCourse.query \
        .filter_by(
            course_id=course.id,
            course_role=CourseRole.student
        ) \
        .all()
    students = {s.user_id: s for s in students}

    # enrol valid users in file
    for user, group_name in imported_users:
        enrol = enroled.get(user.id, UserCourse(course_id=course.id, user_id=user.id))
        enrol.group = None
        if group_name:
            group = groups_by_name.get(group_name)
            # add new groups if needed
            if not group:
                group = Group(
                    course=course,
                    name=group_name
                )
                groups_by_name[group_name] = group
                db.session.add(group)
            enrol.group = group

        # do not overwrite instructor or teaching assistant roles
        if enrol.course_role not in [CourseRole.instructor, CourseRole.teaching_assistant]:
            enrol.course_role = CourseRole.student
            if user.id in students:
                del students[user.id]
            count += 1
        db.session.add(enrol)

    db.session.commit()

    # unenrol users not in file anymore
    for user_id in students:
        enrolment = students.get(user_id)
        # skip users that are already dropped
        if enrolment.course_role == CourseRole.dropped:
            continue
        enrolment.course_role = CourseRole.dropped
        enrolment.group_id = None
        db.session.add(enrolment)
    db.session.commit()

    # wait until user ids are generated before starting background jobs
    # perform password update in chunks of 100
    chunk_size = 100
    chunks = [set_user_passwords[index:index + chunk_size] for index in range(0, len(set_user_passwords), chunk_size)]
    for chunk in chunks:
        set_passwords.delay({
            user.id: password for (user, password) in chunk
        })

    on_classlist_upload.send(
        current_app._get_current_object(),
        event_name=on_classlist_upload.name,
        user=current_user,
        course_id=course.id
    )

    return {
        'success': count,
        'invalids': marshal(invalids, dataformat.get_import_users_results(False))
    }