Exemplo n.º 1
0
class PushedMockTest(db.Model):
    __tablename__ = 'pushed_mock_tests'
    id = db.Column(db.Integer, primary_key=True)
    mock_test_id = db.Column(db.Integer, db.ForeignKey('mock_tests.id'))
    batch_id = db.Column(db.Integer, db.ForeignKey('batches.id'))
    pushed_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    expires_at = db.Column(db.DateTime)
class StudentBatches(db.Model):
    __tablename__ = 'student_batches'
    __table_args__ = (db.UniqueConstraint('student_id', 'batch_id'), )
    id = db.Column(db.Integer, primary_key=True)
    batch_id = db.Column(db.Integer, db.ForeignKey('batches.id'))
    student_id = db.Column(db.Integer, db.ForeignKey('students.id'))
    joined_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    left_at = db.Column(db.DateTime)
Exemplo n.º 3
0
class UserTypes(db.Model):
    __tablename__ = 'user_types'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(
        db.Enum('student',
                'teacher',
                'data_operator',
                'intern',
                'institutes',
                name='user_types_enum'))
Exemplo n.º 4
0
class CategoryApproval(db.Model):
    __tablename__ = 'category_approvals'
    id = db.Column(db.Integer, primary_key=True)
    submission_id = db.Column(
        db.Integer, db.ForeignKey('category_submissions.id',
                                  ondelete='CASCADE'))
    approved_by_type = db.Column(db.Integer, db.ForeignKey('user_types.id'))
    approved_by_id = db.Column(db.Integer)
    approved_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)

    @classmethod
    def create(cls, submission_id, approved_by_type, approved_by_id):
        approval = cls(submission_id=submission_id,
                       approved_by_type=approved_by_type,
                       approved_by_id=approved_by_id)
        db.session.add(approval)
        db.session.commit()
class AttemptedMockTest(db.Model):
    __tablename__ = 'attempted_mock_tests'
    id = db.Column(db.Integer, primary_key=True)
    pushed_mock_test_id = db.Column(
        db.Integer, db.ForeignKey(
            'pushed_mock_tests.id',
            ondelete="CASCADE"))  # null when independently test chosen
    mock_test_id = db.Column(
        db.Integer, db.ForeignKey('mock_tests.id', ondelete="CASCADE"))
    student_id = db.Column(db.Integer,
                           db.ForeignKey('students.id', ondelete="CASCADE"))
    answers = db.Column(
        JSON
    )  # pg json with question id as key and chosen options, time, is_correct, marks as values
    analysis = db.Column(JSON)  # pg json with static analysis data
    score = db.Column(db.Float)
    attempted_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    pdf_report_url = db.Column(db.String)
class PastExamResult(db.Model):
    __tablename__ = 'past_exam_results'
    __table_args__ = (db.UniqueConstraint('year', 'exam'), )
    id = db.Column(db.Integer, primary_key=True)
    year = db.Column(db.Integer)
    exam = db.Column(
        db.Enum(*app.config['TARGET_EXAMS'].keys(), name='target_exams_enum'))
    data = db.Column(
        JSON)  # json containing cutoff, marks-rank map, rank-college map

    @classmethod
    def insert(cls, year, exam, data):
        try:
            exam_result = cls(year=year, exam=exam, data=json.dumps(data))
            db.session.add(exam_result)
            db.session.commit()
        except IntegrityError:
            exam_result = cls.query.filter(cls.year == year,
                                           cls.exam == exam).first()
            exam_result.data = json.dumps(data)
            db.session.commit()
        return exam_result.id
class QuestionUploadSet(db.Model):

    __tablename__ = 'upload_sets'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(
        db.String(app.config['QUESTION_UPLOAD_SET_NAME_MAX_LENGTH']))
    created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    errors_exist = db.Column(db.Boolean, default=False)
    mock_test_id = db.Column(db.Integer, db.ForeignKey('mock_tests.id'))
    questions_added = db.Column(db.Boolean, default=False)
    parsed_questions = db.Column(JSON)
    parsed_comprehensions = db.Column(JSON)

    @property
    def parsed_questions_decoded(self):
        questions = json.loads(self.parsed_questions)
        return questions

    @property
    def parsed_comprehensions_decoded(self):
        comprehensions = json.loads(self.parsed_comprehensions)
        return comprehensions

    @property
    def mock_test(self):
        mock_test = MockTest.query.get(self.mock_test_id)
        return mock_test

    @classmethod
    def create(cls, name, errors_exist, mock_test_id, parsed_questions,
               parsed_comprehensions):
        upload_set = cls(name=name,
                         errors_exist=errors_exist,
                         mock_test_id=mock_test_id,
                         parsed_questions=parsed_questions,
                         parsed_comprehensions=parsed_comprehensions)
        db.session.add(upload_set)
        db.session.commit()

        return upload_set

    @classmethod
    def delete(cls, id):
        upload_set = cls.query.get(id)
        if upload_set is not None:
            cls.query.filter_by(id=id).delete()
            db.session.commit()
class Comprehension(db.Model):
    __tablename__ = 'comprehensions'
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.Text)
    questions = db.relationship('Question',
                                backref='comprehension',
                                lazy='dynamic')

    @classmethod
    def create(cls, content):
        """
        Create a comprehension and return the comprehension object

        :param content: text of comprehension
        :return: comprehension object
        """
        content = Question.parse_content(content)
        comprehension = cls(content=content)
        db.session.add(comprehension)
        db.session.commit()
        return comprehension

    @classmethod
    def update(cls, id, content):
        """
        Update the content of a comprehension

        :param id: the comprehension id
        :param content: text of comprehension
        :return: comprehension object
        """
        content = Question.parse_content(content)
        comprehension = cls.query.get(id)
        comprehension.content = content
        db.session.commit()
        return comprehension
Exemplo n.º 9
0
class User(object):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(app.config['EMAIL_MAX_LENGTH']), unique=True)
    name = db.Column(db.String(app.config['NAME_MAX_LENGTH']))
    password = db.Column(db.String(app.config['PASSWORD_MAX_LENGTH']))
    joined_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    last_activity = db.Column(db.DateTime)
    is_active = db.Column(db.Boolean, default=True)

    @validates('email')
    def validate_email(self, key, email_id):
        m = email_pattern.search(email_id)
        if m is None:
            raise UnAcceptableEmail
        return email_id

    @classmethod
    def authenticate_by_email(cls, email, password):
        """
        Used in login when user explicitly logs in

        :param email:
        :param password:
        :return: the user row if authentication successful, none otherwise
        """
        user = cls.query.filter(cls.email == email,
                                cls.password == password).first()
        return user

    @classmethod
    def authenticate_by_id(cls, id, password):
        """
        Used in basic auth verification that happens on each request

        :param id: user id
        :param password:
        :return: the user row if authentication successful, none otherwise
        """
        user = cls.query.filter(cls.id == id, cls.password == password).first()
        return user

    def update_last_active_to_now(self):
        u = self.__class__.query.get(self.id)
        u.last_activity = datetime.datetime.utcnow()
        db.session.commit()
Exemplo n.º 10
0
class Student(User, db.Model):
    __tablename__ = 'students'
    type = db.Column(db.Integer, db.ForeignKey('user_types.id'), nullable=False)
    mobile_no = db.Column(db.String(app.config['MOBILE_NO_MAX_LENGTH']), unique=True)
    city = db.Column(db.String(100))
    area = db.Column(db.String(100))
    pin = db.Column(db.String(10))
    school = db.Column(db.String(100))
    ntse_score = db.Column(db.Float)
    roll_no = db.Column(db.String(20))
    branches = db.Column(ARRAY(db.String(1)))
    target_exams = db.Column(ARRAY(db.String(1)))
    target_exam_roll_nos = db.Column(ARRAY(db.String(100)))
    target_year = db.Column(db.Integer)
    father_name = db.Column(db.String(app.config['NAME_MAX_LENGTH']))
    father_mobile_no = db.Column(db.String(app.config['MOBILE_NO_MAX_LENGTH']))
    father_email = db.Column(db.String(app.config['EMAIL_MAX_LENGTH']))
    payment_plan_id = db.Column(db.Integer, db.ForeignKey('payment_plans.id'))
    registered_from = db.Column(db.Enum('institute', 'independent', name='registered_from_enum'))
    fp_token = db.Column(db.String(64))
    refcode = db.Column(db.String(200), nullable=True)

    @classmethod
    def create(cls, name, email, password, mobile_no=None, city=None, area=None, pin=None, school=None, ntse_score=None,
               roll_no=None, branches=None, target_exams=None, target_exam_roll_nos=None, target_year=None, father_name=None,
               father_mobile_no=None, father_email=None, payment_plan_id=None, registered_from=None, refcode=None):
        """

        :param name:
        :param email:
        :param password:
        :param mobile_no:
        :param city:
        :param area:
        :param pin:
        :param school:
        :param ntse_score:
        :param roll_no:
        :param branches:
        :param target_exams:
        :param target_exam_roll_nos:
        :param target_year:
        :param father_name:
        :param father_email:
        :param father_mobile_no:
        :param payment_plan_id:
        :param registered_from:
        :return:
        """
        user_type = UserTypes.query.filter_by(name='student').first()
        student = cls(name=name, email=email, password=password, mobile_no=mobile_no, city=city, area=area, pin=pin,
                      school=school, ntse_score=ntse_score, roll_no=roll_no, branches=branches, target_exams=target_exams,
                      target_exam_roll_nos=target_exam_roll_nos, target_year=target_year, father_name=father_name,
                      father_mobile_no=father_mobile_no, payment_plan_id=payment_plan_id, registered_from=registered_from,
                      type=user_type.id, refcode=refcode)
        db.session.add(student)
        try:
            db.session.commit()
        except IntegrityError as e:
            db.session.rollback()
            if 'email' in e.message:
                raise EmailAlreadyRegistered
            if 'mobile_no' in e.message:
                raise MobileNoAlreadyRegistered
            raise e
        return student

    @classmethod
    def get(cls, id):
        """
        Get details of a single student

        :param id: student id
        :return: student object
        """
        student = cls.query.get(id)
        if student is not None:
            return student
        else:
            raise InvalidStudentId

    @classmethod
    def get_list(cls, page=1, limit=10):
        """
        Get a list of students

        :param page:
        :param limit:
        :return:
        """
        students_pag_obj = cls.query.filter_by(is_active=True).paginate(page, limit)
        students = students_pag_obj.items
        total = students_pag_obj.total

        return students, total

    @classmethod
    def get_attempted_mock_tests(cls, id):
        """

        :param id:
        :return:
        """
        attempted_mock_tests = AttemptedMockTest.query.filter_by(student_id=id).all()
        return attempted_mock_tests

    @classmethod
    def get_pushed_mock_tests(cls, id):
        """

        :param id:
        :return:
        """
        # get batches that the student is currently part of
        student_current_batches = StudentBatches.query.filter(StudentBatches.student_id == id, StudentBatches.left_at == None).all()
        if len(student_current_batches) > 0:
            # get mock tests that are pushed to batches that the student is currently part of and have not expired
            batch_ids = [sb.batch_id for sb in student_current_batches]

            pushed_mock_tests = PushedMockTest.query.filter(PushedMockTest.batch_id.in_(batch_ids),
                                                        or_(PushedMockTest.expires_at == None, PushedMockTest.expires_at > datetime.datetime.utcnow())).all()
        else:
            pushed_mock_tests = []

        return pushed_mock_tests
Exemplo n.º 11
0
class Question(db.Model):
    __tablename__ = 'questions'
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.Text, nullable=False)
    status = db.Column(MutableDict.as_mutable(HSTORE),
                       default={})  # contains flags with values 0 and 1
    comprehension_id = db.Column(
        db.Integer, db.ForeignKey('comprehensions.id', ondelete='CASCADE'))
    all_options = db.Column(ARRAY(db.Text), default=[])
    correct_options = db.Column(ARRAY(db.Integer), default=[])
    option_reasons = db.Column(ARRAY(db.Text), default=[])
    average_time = db.Column(db.Integer)
    ontology = db.Column(ARRAY(db.Integer))
    nature = db.Column(
        db.Enum(*app.config['QUESTION_NATURE'].keys(),
                name='question_nature_enum'))
    difficulty = db.Column(
        db.Enum(*app.config['QUESTION_DIFFICULTY_LEVEL'],
                name='question_difficulty_enum'))
    type = db.Column(
        db.Enum(*app.config['QUESTION_TYPE'], name='question_type_enum'))
    text_solution = db.Column(db.Text)  # null until approved
    video_solution_url = db.Column(db.String(
        app.config['URL_MAX_LENGTH']))  # null until approved
    text_solution_by_type = db.Column(db.Integer,
                                      db.ForeignKey('user_types.id'))
    text_solution_by_id = db.Column(db.Integer)
    video_solution_by_type = db.Column(db.Integer,
                                       db.ForeignKey('user_types.id'))
    video_solution_by_id = db.Column(db.Integer)
    similar_question_ids = db.Column(ARRAY(db.Integer))  # null if unfilled
    created_by_type = db.Column(db.Integer, db.ForeignKey('user_types.id'))
    created_by_id = db.Column(db.Integer)
    created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    is_similarity_marked = db.Column(db.Boolean, default=False)

    @validates('video_solution_url')
    def validate_video_url(self, key, url):
        if url is None:
            return url
        parsed_url = urlparse.urlparse(url)
        netloc = parsed_url.netloc
        if netloc not in ('youtube.com', 'www.youtube.com', 'youtu.be',
                          'www.youtu.be'):
            raise UnAcceptableVideoUrl
        return url

    img_base64_pat = re.compile('<img [^>]*?src="(data:image.*?)".*?>')

    @classmethod
    def parse_content(cls, content):
        """
        Parse and return the content. Convert base64 images to s3 image urls.

        :param content:
        :return: string
        """
        match = cls.img_base64_pat.search(content)
        s3obj = S3()
        while match is not None:
            mimetype, image_data = parse_base64_string(match.groups()[0])
            url = s3obj.upload(image_data, mimetype)
            img_tag = '<img src="%s" />' % url
            content = content[:match.start()] + img_tag + content[match.end():]
            match = cls.img_base64_pat.search(content)

        return content

    @classmethod
    def create(cls,
               content,
               status,
               comprehension_id=None,
               all_options=None,
               correct_options=None,
               option_reasons=None,
               ontology_id=None,
               average_time=app.config['DEFAULT_AVERAGE_TIME'],
               nature=None,
               difficulty=None,
               type=None,
               text_solution=None,
               video_solution_url=None,
               text_solution_by_type=None,
               text_solution_by_id=None,
               video_solution_by_type=None,
               video_solution_by_id=None,
               similar_question_ids=None,
               created_by_type=None,
               created_by_id=None):
        """
        Create a new question and return the newly created object.

        :param content: question test
        :param status: status dict
        :param comprehension_id: if question is part of comprehension
        :param all_options:
        :param correct_options: indices of correct options
        :param option_reasons:
        :param ontology_id:
        :param average_time:
        :param nature:
        :param difficulty:
        :param type:
        :param text_solution:
        :param video_solution_url:
        :param text_solution_by_type: the type of user if text solution was provided
        :param text_solution_by_id:   the id of user if text solution was provided
        :param video_solution_by_type: the type of user if video solution was provided
        :param video_solution_by_id:    the id of user if video solution was provided
        :param similar_question_ids:    array of question ids
        :param created_by_type:     the type of user who entered this question
        :param created_by_id:       the id of user who entered this question
        :return:
        """
        if ontology_id is not None:
            ontology_obj = Ontology.query.get(ontology_id)
            if ontology_obj is None:
                raise InvalidOntologyNodeId
            ontology = ontology_obj.absolute_path
        else:
            ontology = None

        content = content.strip()
        content = cls.parse_content(content)

        if all_options is not None:
            for i, option in enumerate(all_options):
                if option is not None:
                    all_options[i] = cls.parse_content(option)

        if option_reasons is not None:
            for i, reason in enumerate(option_reasons):
                if reason is not None:
                    option_reasons[i] = cls.parse_content(reason)

        if text_solution is not None:
            text_solution = cls.parse_content(text_solution)

        question = cls(content=content,
                       status=status,
                       comprehension_id=comprehension_id,
                       all_options=all_options,
                       correct_options=correct_options,
                       option_reasons=option_reasons,
                       ontology=ontology,
                       average_time=average_time,
                       nature=nature,
                       difficulty=difficulty,
                       type=type,
                       text_solution=text_solution,
                       video_solution_url=video_solution_url,
                       text_solution_by_type=text_solution_by_type,
                       text_solution_by_id=text_solution_by_id,
                       video_solution_by_type=video_solution_by_type,
                       video_solution_by_id=video_solution_by_id,
                       similar_question_ids=similar_question_ids,
                       created_by_type=created_by_type,
                       created_by_id=created_by_id)
        db.session.add(question)
        db.session.commit()
        return question

    @classmethod
    def get(cls, id):
        """
        Get details of a single question

        :param id: question id
        :return: question object
        """
        question = cls.query.get(id)
        if question is not None:
            if question.text_solution is not None or question.video_solution_url is not None:
                ss = SolutionSubmission.query.filter_by(
                    question_id=question.id).all()
                for item in ss:
                    if item.solution_type == 'video':
                        video_submission = item
                    if item.solution_type == 'text':
                        text_submission = item
                if question.text_solution is not None:
                    question.text_solution_by_type = text_submission.submitted_by_type
                    question.text_solution_by_id = text_submission.submitted_by_id
                if question.video_solution_url is not None:
                    question.video_solution_by_type = video_submission.submitted_by_type
                    question.video_solution_by_id = video_submission.submitted_by_id
            return question
        else:
            raise InvalidQuestionId

    def update(self,
               content,
               status,
               comprehension_id=None,
               all_options=None,
               correct_options=None,
               option_reasons=None,
               ontology_id=None,
               average_time=app.config['DEFAULT_AVERAGE_TIME'],
               nature=None,
               difficulty=None,
               type=None,
               text_solution=None,
               video_solution_url=None,
               text_solution_by_type=None,
               text_solution_by_id=None,
               video_solution_by_type=None,
               video_solution_by_id=None,
               similar_question_ids=None):
        """
        Update the details of single question

        :param id: question_id
        :param content:
        :param status: status dict
        :param comprehension_id: if question is part of comprehension
        :param all_options:
        :param correct_options: indices of correct options
        :param option_reasons:
        :param ontology_id:
        :param average_time:
        :param nature:
        :param difficulty:
        :param type:
        :param text_solution:
        :param video_solution_url:
        :param text_solution_by_type: the type of user if text solution was provided
        :param text_solution_by_id:   the id of user if text solution was provided
        :param video_solution_by_type: the type of user if video solution was provided
        :param video_solution_by_id:    the id of user if video solution was provided
        :param similar_question_ids:    array of question ids
        :return:
        """
        if ontology_id is not None:
            ontology_obj = Ontology.query.get(ontology_id)
            if ontology_obj is None:
                raise InvalidOntologyNodeId
            ontology = ontology_obj.absolute_path
        else:
            ontology = None

        content = self.__class__.parse_content(content)

        if all_options is not None:
            for i, option in enumerate(all_options):
                if option is not None:
                    all_options[i] = self.__class__.parse_content(option)

        if option_reasons is not None:
            for i, reason in enumerate(option_reasons):
                if reason is not None:
                    option_reasons[i] = self.__class__.parse_content(reason)

        if text_solution is not None:
            text_solution = self.__class__.parse_content(text_solution)

        question = self
        question.content = content
        question.status = status
        question.comprehension_id = comprehension_id
        question.all_options = all_options
        question.correct_options = correct_options
        question.option_reasons = option_reasons
        question.ontology = ontology
        question.average_time = average_time
        question.nature = nature
        question.difficulty = difficulty
        question.type = type
        question.text_solution = text_solution
        question.video_solution_url = video_solution_url
        question.text_solution_by_type = text_solution_by_type
        question.text_solution_by_id = text_solution_by_id
        question.video_solution_by_type = video_solution_by_type
        question.video_solution_by_id = video_solution_by_id
        question.similar_question_ids = similar_question_ids
        db.session.commit()
        return question

    @classmethod
    def get_filtertered_list(cls,
                             nature=None,
                             type=None,
                             difficulty=None,
                             average_time=None,
                             categorized=None,
                             proof_read_categorization=None,
                             proof_read_text_solution=None,
                             proof_read_video_solution=None,
                             finalized=None,
                             error_reported=None,
                             text_solution_added=None,
                             video_solution_added=None,
                             ontology=None,
                             is_comprehension=None,
                             similar_questions_to=None,
                             not_similar_questions_to=None,
                             exclude_question_ids=None,
                             include_question_ids=None,
                             page=1,
                             limit=None):
        """
        Get a list of question after applying filters

        :param nature:
        :param type:
        :param difficulty:
        :param average_time:
        :param categorized:
        :param proof_read_categorization:
        :param proof_read_text_solution:
        :param proof_read_video_solution:
        :param finalized:
        :param text_solution_added:
        :param video_solution_added:
        :param ontology:
        :param is_comprehension:
        :param similar_questions_to: questions which are similar to the question id
        :param not_similar_questions_to: questions which are not similar to the question id
        :param exclude_question_ids: dont return questions with these ids
        :param include_question_ids: return questions with these ids
        :param page:
        :param limit:
        :return:
        """

        exprs = []

        if nature is not None:
            exprs.append(Question.nature == nature)

        if type is not None:
            exprs.append(Question.type == type)

        if difficulty is not None:
            exprs.append(Question.difficulty == difficulty)

        if average_time is not None:
            exprs.append(Question.average_time == average_time)

        if categorized is not None:
            exprs.append(Question.status['categorized'] == categorized)

        if proof_read_categorization is not None:
            exprs.append(Question.status['proof_read_categorization'] ==
                         proof_read_categorization)

        if proof_read_text_solution is not None:
            exprs.append(Question.status['proof_read_text_solution'] ==
                         proof_read_text_solution)

        if proof_read_video_solution is not None:
            exprs.append(Question.status['proof_read_video_solution'] ==
                         proof_read_video_solution)

        if finalized is not None:
            exprs.append(Question.status['finalized'] == finalized)

        if error_reported is not None:
            exprs.append(Question.status['error_reported'] == error_reported)

        if ontology is not None:
            exprs.append(Question.ontology.contains(ontology))

        if is_comprehension is not None:
            if is_comprehension == 0:
                exprs.append(Question.comprehension_id == None)
            else:
                exprs.append(Question.comprehension_id != None)

        if text_solution_added is not None:
            exprs.append(Question.status['text_solution_added'] == str(
                text_solution_added))

        if video_solution_added is not None:
            exprs.append(Question.status['video_solution_added'] == str(
                video_solution_added))

        if exclude_question_ids is not None:
            exprs.append(not_(Question.id.in_(exclude_question_ids)))

        if include_question_ids is not None:
            exprs.append(Question.id.in_(include_question_ids))

        if similar_questions_to is not None or not_similar_questions_to is not None:
            # Need to select only categorized questions
            categorized_expr = Question.status['categorized'] == '1'
            if categorized_expr not in exprs:
                exprs.append(categorized_expr)

            if similar_questions_to is not None:
                # questions need to be compared only when ontology matches exactly
                ques = cls.get(similar_questions_to)
                exprs.append(Question.ontology == ques.ontology)
                # exclude itself
                exprs.append(Question.id != ques.id)
                exprs.append(
                    Question.similar_question_ids.any(similar_questions_to))

            if not_similar_questions_to is not None:
                # questions need to be compared only when ontology matches exactly
                ques = cls.get(not_similar_questions_to)
                exprs.append(Question.ontology == ques.ontology)
                # exclude itself
                exprs.append(Question.id != ques.id)
                exprs.append(
                    or_(
                        not_(
                            Question.similar_question_ids.any(
                                not_similar_questions_to)),
                        Question.similar_question_ids == None))
        if limit is not None:
            questions_pag_obj = Question.query.filter(*exprs).order_by(
                Question.created_at.desc()).paginate(page, limit)
            questions = questions_pag_obj.items
            total = questions_pag_obj.total
        else:
            q = Question.query.filter(*exprs).order_by(
                Question.created_at.desc())
            questions = q.all()
            total = len(questions)

        question_dict = OrderedDict()
        for q in questions:
            question_dict[q.id] = q

        submissions = SolutionSubmission.query.filter(
            SolutionSubmission.question_id.in_(question_dict.keys())).all()
        for item in submissions:
            question = question_dict[item.question_id]
            if item.solution_type == 'text' and question.text_solution is not None:
                question.text_solution_by_type = item.submitted_by_type
                question.text_solution_by_id = item.submitted_by_id
            if item.solution_type == 'video' and question.video_solution_url is not None:
                question.video_solution_by_type = item.submitted_by_type
                question.video_solution_by_id = item.submitted_by_id

        return {'questions': question_dict.values(), 'total': total}

    @classmethod
    def reset_categorization(cls, id):
        """
        Reset the categorization flag and categorization fields

        :param id:
        :return:
        """
        question = cls.query.get(id)
        question.status['categorized'] = '0'
        question.status['proof_read_categorization'] = '0'
        question.status['finalized'] = '0'
        if question.ontology is not None:
            question.ontology = question.ontology[0:1]
        question.average_time = None
        question.nature = None
        question.difficulty = None
        question.type = None
        db.session.commit()
        return question

    @classmethod
    def approve_categorization(cls, id):
        """
        Approve the categorization. Set the categorization flag and proof read categorization flag

        :param id:
        :return:
        """
        question = cls.query.get(id)
        question.status['categorized'] = '1'
        question.status['proof_read_categorization'] = '1'
        if '0' not in (question.status['proof_read_categorization'],
                       question.status['proof_read_text_solution'],
                       question.status['proof_read_video_solution']):
            # question has been proof read in all 3 respects so its finalized
            question.status['finalized'] = '1'
        db.session.commit()
        return question

    @classmethod
    def reset_solution(cls, id, sol_type):
        """
        Reset the solution flags and solution field

        :param id:
        :param sol_type: Solution type. Can be text or video
        :return:
        """
        question = cls.query.get(id)
        if sol_type == 'text':
            question.status['text_solution_added'] = '0'
            question.status['proof_read_text_solution'] = '0'
            question.text_solution = None
            question.text_solution_by_type = None
            question.text_solution_by_id = None

        if sol_type == 'video':
            question.status['video_solution_added'] = '0'
            question.status['proof_read_video_solution'] = '0'
            question.video_solution_url = None
            question.video_solution_by_type = None
            question.video_solution_by_id = None

        question.status['finalized'] = '0'
        db.session.commit()
        return question

    @classmethod
    def approve_solution(cls, id, sol_type):
        """
        Approve the solution. Set the solution flag and proof read solution flag

        :param id:
        :param sol_type: Solution type. Can be text or video
        :return:
        """
        question = cls.query.get(id)
        if sol_type == 'text':
            question.status['text_solution_added'] = '1'
            question.status['proof_read_text_solution'] = '1'

        if sol_type == 'video':
            question.status['video_solution_added'] = '1'
            question.status['proof_read_video_solution'] = '1'

        if '0' not in (question.status['proof_read_categorization'],
                       question.status['proof_read_text_solution'],
                       question.status['proof_read_video_solution']):
            # question has been proof read in all 3 respects so its finalized
            question.status['finalized'] = '1'
        db.session.commit()
        return question
Exemplo n.º 12
0
class MockTest(db.Model):
    __tablename__ = 'mock_tests'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(app.config['TEST_NAME_MAX_LENGTH']))
    created_by_type = db.Column(db.Integer, db.ForeignKey('user_types.id'))
    created_by_id = db.Column(db.Integer)
    created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    question_ids = db.Column(JSON)
    target_exam = db.Column(
        db.Enum(*app.config['TARGET_EXAMS'].keys(), name='target_exams_enum'))
    difficulty = db.Column(
        db.Enum(*app.config['MOCK_TEST_DIFFICULTY_LEVEL'].keys(),
                name='mock_test_difficulty_enum'))
    description = db.Column(db.Text)
    for_institutes = db.Column(db.Boolean, default=False)
    is_locked = db.Column(db.Boolean, default=False)
    type = db.Column(
        db.Enum(*app.config['MOCK_TEST_TYPES'].keys(),
                name='mock_test_types_enum'))
    type_id = db.Column(db.Integer,
                        db.ForeignKey('ontology.id', ondelete='CASCADE'))
    prerequisite_id = db.Column(
        db.Integer, db.ForeignKey('mock_tests.id', ondelete='CASCADE'))
    duration = db.Column(db.Integer)
    syllabus = db.Column(db.Text)
    cutoff = db.Column(db.Float)
    date_closed = db.Column(
        db.Boolean,
        default=False)  # will the test open on a date or is it by default open
    opening_date = db.Column(db.Date, nullable=True)

    @classmethod
    def create(cls,
               name,
               difficulty,
               target_exam,
               for_institutes,
               question_ids,
               type,
               type_id,
               prerequisite_id,
               duration,
               created_by_type,
               created_by_id,
               date_closed,
               description=None,
               syllabus=None,
               cutoff=None,
               opening_date=None):
        mock_test = cls(name=name,
                        difficulty=difficulty,
                        target_exam=target_exam,
                        for_institutes=for_institutes,
                        question_ids=question_ids,
                        description=description,
                        syllabus=syllabus,
                        type=type,
                        type_id=type_id,
                        prerequisite_id=prerequisite_id,
                        duration=duration,
                        cutoff=cutoff,
                        created_by_type=created_by_type,
                        created_by_id=created_by_id,
                        date_closed=date_closed,
                        opening_date=opening_date)

        db.session.add(mock_test)
        db.session.commit()
        return mock_test
Exemplo n.º 13
0
class Teacher(User, db.Model):
    __tablename__ = 'teachers'
    type = db.Column(db.Integer,
                     db.ForeignKey('user_types.id'),
                     nullable=False)
    subject_expert = db.Column(db.String(100))
    specialization = db.Column(db.String(100))
    qualification = db.Column(db.String(100))

    @classmethod
    def create(cls,
               name,
               email,
               password,
               subject_expert=None,
               specialization=None,
               qualification=None):
        """
        Create a new teacher. If need encrypted password, then provide one. No encryption is done in this function.

        :param name:
        :param email:
        :param password:
        :param subject_expert:
        :param specialization:
        :param qualification:
        :return:
        """
        user_type = UserTypes.query.filter_by(name='teacher').first()
        teacher = cls(name=name,
                      email=email,
                      password=password,
                      subject_expert=subject_expert,
                      specialization=specialization,
                      qualification=qualification,
                      type=user_type.id)
        db.session.add(teacher)
        try:
            db.session.commit()
        except IntegrityError:
            db.session.rollback()
            raise EmailAlreadyRegistered
        return teacher

    @classmethod
    def get_list(cls, page=1, limit=10):
        """
        Get a list of active teacher with their stats

        :param page:
        :param limit:
        :return:
        """
        result = {}

        pag_obj = cls.query.filter_by(is_active=True).paginate(page, limit)
        teachers = pag_obj.items
        total = pag_obj.total
        if total == 0:
            return [], 0

        for op in teachers:
            result[op.id] = {'teacher': op}
        teacher_type = teachers[0].type
        teacher_ids = result.keys()

        reported_resolved_count = db.session.query(
            ReportedQuestion.resolved_by_id,
            func.count(ReportedQuestion.id)).filter(
                ReportedQuestion.resolved_by_type == teacher_type,
                ReportedQuestion.resolved_by_id.in_(teacher_ids),
                ReportedQuestion.is_resolved == True).group_by(
                    ReportedQuestion.resolved_by_id)
        for id, count in reported_resolved_count:
            result[id]['reported_resolved'] = count

        cat_sub_counts = db.session.query(CategorySubmission.submitted_by_id, func.count(CategorySubmission.id)).filter(
            CategorySubmission.submitted_by_type == teacher_type, CategorySubmission.submitted_by_id.in_(teacher_ids))\
            .group_by(CategorySubmission.submitted_by_id)
        for id, count in cat_sub_counts:
            result[id]['questions_categorized'] = count

        cat_app_counts = db.session.query(CategoryApproval.approved_by_id, func.count(CategoryApproval.id))\
            .filter(CategoryApproval.approved_by_id.in_(teacher_ids), CategoryApproval.approved_by_type == teacher_type).group_by(CategoryApproval.approved_by_id)
        for id, count in cat_app_counts:
            result[id]['questions_approved'] = count

        sol_counts = db.session.query(SolutionSubmission.submitted_by_id, SolutionSubmission.solution_type, func.count(SolutionSubmission.id))\
            .filter(SolutionSubmission.submitted_by_type == teacher_type, SolutionSubmission.submitted_by_id.in_(teacher_ids))\
            .group_by(SolutionSubmission.submitted_by_id, SolutionSubmission.solution_type)
        for id, sol_type, count in sol_counts:
            if sol_type == 'text':
                result[id]['text_solutions_submitted'] = count
            if sol_type == 'video':
                result[id]['video_solutions_submitted'] = count

        sol_app_counts = db.session.query(SolutionApproval.approved_by_id, SolutionSubmission.solution_type, func.count(SolutionApproval.id))\
            .filter(SolutionApproval.approved_by_type == teacher_type, SolutionApproval.approved_by_id.in_(teacher_ids),
                    SolutionApproval.submission_id == SolutionSubmission.id).group_by(SolutionApproval.approved_by_id,
                                                                                      SolutionSubmission.solution_type)

        for id, sol_type, count in sol_app_counts:
            if sol_type == 'text':
                result[id]['text_solutions_approved'] = count
            if sol_type == 'video':
                result[id]['video_solutions_approved'] = count

        return result.values(), total

    @classmethod
    def get(cls, id):
        """
        Get a single teacher with his stats

        :param id:
        :return:
        """

        teacher = cls.query.get(id)
        if teacher is None:
            raise InvalidTeacherId

        reported_resolved = db.session.query(func.count(
            ReportedQuestion.id)).filter(
                ReportedQuestion.is_resolved == True,
                ReportedQuestion.resolved_by_type == teacher.type,
                ReportedQuestion.resolved_by_id == teacher.id).first()[0]

        cat_sub_count = db.session.query(func.count(
            CategorySubmission.id)).filter(
                CategorySubmission.submitted_by_type == teacher.type,
                CategorySubmission.submitted_by_id == teacher.id).first()[0]

        cat_app_count = db.session.query(func.count(CategoryApproval.id))\
            .filter(CategorySubmission.submitted_by_type == teacher.type, CategorySubmission.submitted_by_id == teacher.id, CategoryApproval.submission_id == CategorySubmission.id).first()[0]

        sol_counts = db.session.query(SolutionSubmission.solution_type, func.count(SolutionSubmission.id))\
            .filter(SolutionSubmission.submitted_by_type == teacher.type, SolutionSubmission.submitted_by_id == teacher.id)\
            .group_by(SolutionSubmission.solution_type)

        text_solutions_submitted = 0
        video_solutions_submitted = 0
        for sol_type, count in sol_counts:
            if sol_type == 'text':
                text_solutions_submitted = count
            if sol_type == 'video':
                video_solutions_submitted = count

        sol_app_counts = db.session.query(SolutionSubmission.solution_type, func.count(SolutionApproval.id))\
            .filter(CategorySubmission.submitted_by_type == teacher.type, CategorySubmission.submitted_by_id == teacher.id, SolutionApproval.submission_id == SolutionSubmission.id)\
            .group_by(SolutionSubmission.solution_type)

        text_solutions_approved = 0
        video_solutions_approved = 0
        for sol_type, count in sol_app_counts:
            if sol_type == 'text':
                text_solutions_approved = count
            if sol_type == 'video':
                video_solutions_approved = count

        return {
            'teacher': teacher,
            'questions_categorized': cat_sub_count,
            'questions_approved': cat_app_count,
            'text_solutions_submitted': text_solutions_submitted,
            'video_solutions_submitted': video_solutions_submitted,
            'text_solutions_approved': text_solutions_approved,
            'video_solutions_approved': video_solutions_approved,
            'reported_resolved': reported_resolved
        }
Exemplo n.º 14
0
class CategorySubmission(db.Model):
    __tablename__ = 'category_submissions'
    id = db.Column(db.Integer, primary_key=True)
    submitted_by_type = db.Column(db.Integer, db.ForeignKey('user_types.id'))
    submitted_by_id = db.Column(db.Integer)

    # JSON in the form of {"ontology": [1,10,13], "type": "", "nature": , ...}
    category = db.Column(JSON)

    question_id = db.Column(db.Integer,
                            db.ForeignKey('questions.id', ondelete='CASCADE'))
    submitted_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    status = db.Column(
        db.Enum(*app.config['SUBMISSION_STATUS'],
                name='submission_status_enum'))

    @classmethod
    def create(cls,
               submitted_by_type,
               submitted_by_id,
               question_id,
               ontology=None,
               nature=None,
               type=None,
               difficulty=None,
               average_time=None,
               status=None):
        """
        Create a new categorization submission

        :param submitted_by_type: the user type who submitted
        :param submitted_by_id: the user id who submitted
        :param question_id: the question id this submission corresponds too
        :param ontology: the ontology node's complete path
        :param type:
        :param difficulty:
        :param average_time:
        :param status:
        :return:
        """
        category = {
            'ontology': ontology,
            'type': type,
            'difficulty': difficulty,
            'average_time': average_time,
            'nature': nature
        }
        submission = cls(submitted_by_type=submitted_by_type,
                         submitted_by_id=submitted_by_id,
                         question_id=question_id,
                         category=json.dumps(category),
                         status=status)
        db.session.add(submission)
        db.session.commit()
        return submission

    @classmethod
    def get(cls,
            nature=None,
            type=None,
            difficulty=None,
            average_time=None,
            ontology=None,
            page=1,
            limit=10):
        """
        Get filtered category submissions

        :param nature:
        :param type:
        :param difficulty:
        :param average_time:
        :param ontology:
        :return:
        """

        data = Question.get_filtertered_list(nature=nature,
                                             type=type,
                                             difficulty=difficulty,
                                             average_time=average_time,
                                             ontology=ontology,
                                             categorized='1',
                                             proof_read_categorization='0',
                                             page=page,
                                             limit=limit)
        if data['total'] > 0:
            temp = OrderedDict()
            for q in data['questions']:
                temp[q.id] = {'question': q}

            submissions = cls.query.filter(cls.question_id.in_(temp.keys()),
                                           cls.status == None).all()
            for submission in submissions:
                temp[submission.question_id]['submission_id'] = submission.id
            return temp.values(), data['total']
        else:
            return [], 0
class SolutionSubmission(db.Model):
    __tablename__ = 'solution_submissions'
    id = db.Column(db.Integer, primary_key=True)
    submitted_by_type = db.Column(db.Integer, db.ForeignKey('user_types.id'))
    submitted_by_id = db.Column(db.Integer)
    solution_type = db.Column(
        db.Enum('text', 'video', name='solution_types_enum'))
    solution = db.Column(db.Text)
    question_id = db.Column(db.Integer,
                            db.ForeignKey('questions.id', ondelete='CASCADE'))
    submitted_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    status = db.Column(
        db.Enum(*app.config['SUBMISSION_STATUS'],
                name='submission_status_enum'))

    @classmethod
    def create(cls,
               submitted_by_type,
               submitted_by_id,
               question_id,
               solution_type,
               solution,
               status=None):
        """
        Create a new solution submission

        :param submitted_by_type: the user type who submitted
        :param submitted_by_id: the user id who submitted
        :param question_id: the question id this submission corresponds too
        :param solution_type: text/video
        :param solution:
        :param status:
        :return:
        """
        submission = cls(submitted_by_type=submitted_by_type,
                         submitted_by_id=submitted_by_id,
                         question_id=question_id,
                         solution_type=solution_type,
                         solution=solution,
                         status=status)
        db.session.add(submission)
        db.session.commit()
        return submission

    @classmethod
    def get(cls,
            nature=None,
            type=None,
            difficulty=None,
            average_time=None,
            ontology=None,
            sol_type='text',
            page=1,
            limit=10):
        """
        Get filtered category submissions

        :param nature:
        :param type:
        :param difficulty:
        :param average_time:
        :param ontology:
        :return:
        """
        if sol_type == 'text':
            data = Question.get_filtertered_list(nature=nature,
                                                 type=type,
                                                 difficulty=difficulty,
                                                 average_time=average_time,
                                                 ontology=ontology,
                                                 text_solution_added='1',
                                                 proof_read_text_solution='0',
                                                 page=page,
                                                 limit=limit)
        else:
            data = Question.get_filtertered_list(nature=nature,
                                                 type=type,
                                                 difficulty=difficulty,
                                                 average_time=average_time,
                                                 ontology=ontology,
                                                 video_solution_added='1',
                                                 proof_read_video_solution='0',
                                                 page=page,
                                                 limit=limit)
        if data['total'] > 0:
            temp = OrderedDict()
            for q in data['questions']:
                temp[q.id] = {'question': q}

            submissions = cls.query.filter(
                cls.question_id.in_(temp.keys()), cls.status == None,
                cls.solution_type == sol_type).all()
            for submission in submissions:
                temp[submission.question_id]['submission_id'] = submission.id
            return temp.values(), data['total']
        else:
            return [], 0
Exemplo n.º 16
0
class PaymentPlan(db.Model):
    __tablename__ = 'payment_plans'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(app.config['NAME_MAX_LENGTH']), unique=True)
    price = db.Column(db.Integer)
Exemplo n.º 17
0
class ReportedQuestion(db.Model):
    __tablename__ = 'reported_questions'
    id = db.Column(db.Integer, primary_key=True)
    reported_by_type = db.Column(db.Integer, db.ForeignKey('user_types.id'))
    reported_by_id = db.Column(db.Integer)
    is_resolved = db.Column(db.Boolean, default=False)
    question_id = db.Column(db.Integer, db.ForeignKey('questions.id', ondelete='CASCADE'))
    reported_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    resolved_by_type = db.Column(db.Integer, db.ForeignKey('user_types.id'))
    resolved_by_id = db.Column(db.Integer)
    resolved_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)


    @classmethod
    def create(cls, reported_by_type, reported_by_id, question_id):
        """
        Report a question

        :param reported_by_type:
        :param reported_by_id:
        :param question_id:
        :return:
        """
        report = cls(reported_by_type=reported_by_type, reported_by_id=reported_by_id, question_id=question_id)
        question = Question.query.get(question_id)
        question.status['error_reported'] = '1'
        db.session.add(report)
        db.session.commit()
        return report


    @classmethod
    def get(cls, nature=None, type=None, difficulty=None, average_time=None, ontology=None, page=1, limit=10):
        """
        Get reported questions

        :param nature:
        :param type:
        :param difficulty:
        :param average_time:
        :param ontology:
        :param page:
        :param limit:
        :return:
        """

        exprs = []

        if nature is not None:
            exprs.append(Question.nature == nature)

        if type is not None:
            exprs.append(Question.type == type)

        if difficulty is not None:
            exprs.append(Question.difficulty == difficulty)

        if average_time is not None:
            exprs.append(Question.average_time == average_time)

        if ontology is not None:
            exprs.append(Question.ontology.contains(ontology))

        result = db.session.query(cls, Question).filter(cls.is_resolved == False, cls.question_id == Question.id, *exprs).order_by(Question.created_at.desc())
        total = result.count()
        reported_questions = []
        for row in result.offset((page-1)*limit).limit(limit):
            reported_questions.append({
                'question': row[1],
                'report_id': row[0].id
            })

        return reported_questions, total

    @classmethod
    def delete(cls, id):
        report = cls.query.get(id)
        if report is not None:
            Question.query.filter_by(id=report.question_id).delete()
            cls.query.filter_by(id=id).delete()
            db.session.commit()
Exemplo n.º 18
0
class Batch(db.Model):
    __tablename__ = 'batches'
    __table_args__ = (db.UniqueConstraint('name', 'institute_id'), )
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(app.config['NAME_MAX_LENGTH']))
    on_weekdays = db.Column(db.Boolean, default=False)
    on_weekends = db.Column(db.Boolean, default=False)
    clazz = db.Column(db.Enum('11', '12', name='classes_enum'))
    target_year = db.Column(db.Integer)
    type = db.Column(db.Enum(*app.config['BATCH_TYPE'].keys(), name='batch_types_enum'))
    target_exam = db.Column(db.Enum(*app.config['TARGET_EXAMS'].keys(), name='target_exams_enum'))
    other = db.Column(db.Text)
    batch_timings = db.Column(db.String(20))
    institute_id = db.Column(db.Integer, db.ForeignKey('institutes.id'))
    created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    status = db.Column(db.Integer, default=1)


    @classmethod
    def create(cls, name, on_weekdays, on_weekends, clazz, target_year, target_exam, type, other, batch_timings, institute_id):
        """
        Create a new batch
        :param name:
        :param on_weekdays:
        :param on_weekends:
        :param clazz:
        :param target_year:
        :param target_exam:
        :param type:
        :param other: some text about batch
        :param batch_timings: string in the form ``h1:m1-h2:m2``
        :param institute_id:
        :return:
        """
        batch = cls(name=name, on_weekdays=on_weekdays, on_weekends=on_weekends, clazz=clazz, target_year=target_year,
            target_exam=target_exam, type=type, other=other, batch_timings=batch_timings, institute_id=institute_id)
        db.session.add(batch)
        try:
            db.session.commit()
        except IntegrityError:
            db.session.rollback()
            raise BatchNameAlreadyTaken
        return batch

    @classmethod
    def get(cls, id):
        """
        Get a single batch

        :param id:
        :return:
        """

        batch = cls.query.get(id)
        if batch is None:
            raise InvalidBatchId
        return batch

    @classmethod
    def get_filtered(cls, days=None, type=None, target_year=None, target_exam=None, include_ids=None, institute_id=None, status=None, branches=None):
        """
        Get a list of batches after applying filters

        :param days:
        :param type:
        :param target_year:
        :param target_exam:
        :param include_ids:
        :param institute_id:
        :param status:
        :param target_exam_list:
        :return:
        """
        exprs = []
        if days is not None:
            if days == 'weekdays':
                exprs.append(Batch.on_weekdays == True)
            if days == 'weekends':
                exprs.append(Batch.on_weekends == True)
        if type is not None:
            exprs.append(Batch.type == type)
        if target_year is not None:
            exprs.append(Batch.target_year == target_year)
        if target_exam is not None:
            exprs.append(Batch.target_exam == target_exam)
        if institute_id is not None:
            exprs.append(Batch.institute_id == institute_id)
        if include_ids is not None and (isinstance(include_ids, list) or isinstance(include_ids, tuple) or isinstance(include_ids, set)):
            exprs.append(Batch.id.in_(list(include_ids)))
        if branches is not None and (isinstance(branches, list) or isinstance(branches, tuple) or isinstance(branches, set)):
            target_exam_list = []
            engineering_exams = ['1', '2', '3']
            medical_exams = ['4', '5']
            if '1' in branches:
                target_exam_list.extend(engineering_exams)
            if '2' in branches:
                target_exam_list.extend(medical_exams)
            exprs.append(Batch.target_exam.in_(list(target_exam_list)))

        if status is None:
            status = 1
        # explicitly ignoring status
        if status != -1:
            exprs.append(Batch.status == status)

        return Batch.query.filter(*exprs).order_by(Batch.created_at.desc()).all()
Exemplo n.º 19
0
class Intern(User, db.Model):
    __tablename__ = 'interns'
    type = db.Column(db.Integer,
                     db.ForeignKey('user_types.id'),
                     nullable=False)

    @classmethod
    def get_list(cls, page=1, limit=10):
        """
        Get a list of active interns with their stats

        :param page:
        :param limit:
        :return:
        """
        result = {}

        pag_obj = cls.query.filter_by(is_active=True).paginate(page, limit)
        interns = pag_obj.items
        total = pag_obj.total
        if total == 0:
            return [], 0

        for intern in interns:
            result[intern.id] = {'intern': intern}
        intern_type = interns[0].type
        intern_ids = result.keys()

        reported_count = db.session.query(ReportedQuestion.reported_by_id, func.count(ReportedQuestion.id)).filter(
            ReportedQuestion.reported_by_type == intern_type, ReportedQuestion.reported_by_id.in_(intern_ids))\
            .group_by(ReportedQuestion.reported_by_id)
        for id, count in reported_count:
            result[id]['reported_questions'] = count

        cat_sub_counts = db.session.query(CategorySubmission.submitted_by_id, func.count(CategorySubmission.id)).filter(
            CategorySubmission.submitted_by_type == intern_type, CategorySubmission.submitted_by_id.in_(intern_ids))\
            .group_by(CategorySubmission.submitted_by_id)
        for id, count in cat_sub_counts:
            result[id]['questions_categorized'] = count

        cat_app_counts = db.session.query(CategorySubmission.submitted_by_id, func.count(CategoryApproval.id))\
            .filter(CategorySubmission.submitted_by_type == intern_type, CategorySubmission.submitted_by_id
                    .in_(intern_ids), CategoryApproval.submission_id == CategorySubmission.id).group_by(CategorySubmission.submitted_by_id)
        for id, count in cat_app_counts:
            result[id]['questions_approved'] = count

        sol_counts = db.session.query(SolutionSubmission.submitted_by_id, SolutionSubmission.solution_type, func.count(SolutionSubmission.id))\
            .filter(SolutionSubmission.submitted_by_type == intern_type, SolutionSubmission.submitted_by_id.in_(intern_ids))\
            .group_by(SolutionSubmission.submitted_by_id, SolutionSubmission.solution_type)
        for id, sol_type, count in sol_counts:
            if sol_type == 'text':
                result[id]['text_solutions_submitted'] = count
            if sol_type == 'video':
                result[id]['video_solutions_submitted'] = count

        sol_app_counts = db.session.query(SolutionSubmission.submitted_by_id, SolutionSubmission.solution_type, func.count(SolutionApproval.id))\
            .filter(SolutionSubmission.submitted_by_type == intern_type, SolutionSubmission.submitted_by_id.in_(intern_ids), SolutionApproval.submission_id == SolutionSubmission.id)\
            .group_by(SolutionSubmission.submitted_by_id, SolutionSubmission.solution_type)

        for id, sol_type, count in sol_app_counts:
            if sol_type == 'text':
                result[id]['text_solutions_approved'] = count
            if sol_type == 'video':
                result[id]['video_solutions_approved'] = count

        return result.values(), total

    @classmethod
    def create(cls, name, email, password):
        """
        Create an intern. If need encrypted password, then provide one. No encryption is done in this function.

        :param name:
        :param email:
        :param password: encrypted/unencrypted password
        :return:
        """
        user_type = UserTypes.query.filter_by(name='intern').first()
        intern = cls(name=name,
                     email=email,
                     password=password,
                     type=user_type.id)
        db.session.add(intern)
        try:
            db.session.commit()
        except IntegrityError:
            db.session.rolback()
            raise EmailAlreadyRegistered

        return intern

    @classmethod
    def get(cls, id):
        """
        Get a single teacher with his stats

        :param id:
        :return:
        """

        intern = cls.query.get(id)
        if intern is None:
            raise InvalidInternId

        reported_count = db.session.query(func.count(
            ReportedQuestion.id)).filter(
                ReportedQuestion.reported_by_type == intern.type,
                ReportedQuestion.reported_by_id == intern.id).first()[0]

        cat_sub_count = db.session.query(func.count(
            CategorySubmission.id)).filter(
                CategorySubmission.submitted_by_type == intern.type,
                CategorySubmission.submitted_by_id == intern.id).first()[0]

        cat_app_count = db.session.query(func.count(CategoryApproval.id))\
            .filter(CategorySubmission.submitted_by_type == intern.type, CategorySubmission.submitted_by_id == intern.id, CategoryApproval.submission_id == CategorySubmission.id).first()[0]

        sol_counts = db.session.query(SolutionSubmission.solution_type, func.count(SolutionSubmission.id))\
            .filter(SolutionSubmission.submitted_by_type == intern.type, SolutionSubmission.submitted_by_id == intern.id)\
            .group_by(SolutionSubmission.solution_type)

        text_solutions_submitted = 0
        video_solutions_submitted = 0
        for sol_type, count in sol_counts:
            if sol_type == 'text':
                text_solutions_submitted = count
            if sol_type == 'video':
                video_solutions_submitted = count

        sol_app_counts = db.session.query(SolutionSubmission.solution_type, func.count(SolutionApproval.id))\
            .filter(CategorySubmission.submitted_by_type == intern.type, CategorySubmission.submitted_by_id == intern.id, SolutionApproval.submission_id == SolutionSubmission.id)\
            .group_by(SolutionSubmission.solution_type)

        text_solutions_approved = 0
        video_solutions_approved = 0
        for sol_type, count in sol_app_counts:
            if sol_type == 'text':
                text_solutions_approved = count
            if sol_type == 'video':
                video_solutions_approved = count

        return {
            'intern': intern,
            'reported_questions': reported_count,
            'questions_categorized': cat_sub_count,
            'questions_approved': cat_app_count,
            'text_solutions_submitted': text_solutions_submitted,
            'video_solutions_submitted': video_solutions_submitted,
            'text_solutions_approved': text_solutions_approved,
            'video_solutions_approved': video_solutions_approved
        }
Exemplo n.º 20
0
class Ontology(db.Model):
    __tablename__ = 'ontology'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(app.config['NAME_MAX_LENGTH']))
    parent_path = db.Column(ARRAY(db.Integer))
    theory = db.Column(db.Text)
    target_exams = db.Column(ARRAY(db.String))
    type = db.Column(
        db.Enum(*app.config['ONTOLOGY_NODE_TYPES'].keys(),
                name='ontology_node_types_enum'))
    clazz = db.Column(
        db.Enum(*app.config['ONTOLOGY_NODE_CLASSES'].keys(),
                name='ontology_node_classes_enum'))
    created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)

    @classmethod
    def create_node(cls,
                    name,
                    theory=None,
                    parent_id=None,
                    target_exams=None,
                    type=None,
                    clazz=None):
        """
        Create a new node in the ontology tree. If no parent id is provided a root node is create

        :param name: name of the node
        :param theory: theory text of the node
        :param parent_id: id of parent node
        :param target_exams: exams the node belongs to. only applies to root nodes of ontology
        :param type: type of node
        :param clazz: class of a node
        :return: the newly created node or an exception if parent_id is invalid, or if target exam is invalid
        """
        parent_path = []
        if parent_id is not None:
            # non root node
            parent = cls.query.get(parent_id)
            if parent is None:
                raise InvalidOntologyNodeId
            else:
                parent_path = parent.parent_path
                parent_path.append(parent_id)
                # only root node can correspond to exams
                target_exams = None
        else:
            # root node
            if target_exams is None or len(target_exams) < 1:
                raise AtleastOneTargetExamNeededForOntologyRootNode
            else:
                if not set(target_exams).issubset(
                        app.config['TARGET_EXAMS'].keys()):
                    raise UnknownTargetExam

        if type is not None:
            if type not in app.config['ONTOLOGY_NODE_TYPES']:
                raise UnknownOntologyNodeType
        if clazz is not None:
            if type not in app.config['ONTOLOGY_NODE_TYPES']:
                raise UnknownOntologyNodeClass

        node = cls(name=name,
                   theory=theory,
                   parent_path=parent_path,
                   target_exams=target_exams,
                   type=type,
                   clazz=clazz)
        db.session.add(node)
        db.session.commit()
        return node

    @classmethod
    def is_leaf_node(cls, node_id):
        """
        Returns `True` on the truth of either of the two conditions:
        1. If the node_id provided is of leaf node or not
        2. If the node_id is of the topic node (This may or may not be a leaf node)

        :param node_id:
        :return: true/false or exception if node_id is not present
        """
        node = cls.query.get(node_id)
        if node is None:
            raise InvalidOntologyNodeId

        _is_topic_node = lambda node_obj: node_obj.type is '3'
        _is_leaf_node = lambda node_id: cls.query.filter(
            cls.parent_path.any(node_id)).first() is None

        if (_is_topic_node(node) or _is_leaf_node(node_id)):
            return True
        return False

    @classmethod
    def delete_leaf_node(cls, node_id):
        """
        Delete a leaf node. If node is a non leaf node, an exception is raised

        :param node_id:
        :return:
        """
        if cls.is_leaf_node(node_id):
            node = cls.query.get(node_id)
            db.session.delete(node)
            db.session.commit()
        else:
            raise CannotDeleteNonLeafOntologyNode

    @classmethod
    def update_node_theory(cls, node_id, theory):
        """
        Update theory text of a leaf node. If node is a non leaf node, an exception is raised

        :param node_id:
        :param theory:
        :return:
        """
        if cls.is_leaf_node(node_id):
            node = cls.query.get(node_id)
            node.theory = theory
            db.session.commit()
            return node
        else:
            raise CannotUpdateTheoryOfNonLeafOntologyNode

    @classmethod
    def get_all_children_of_node(cls, node_id, get_theory=True):
        """
        Returns a list of children nodes of a node

        :param node_id:
        :param get_theory: if True return theory also else provide a boolean `theory_exists`
        :return: a list
        """
        if get_theory:
            return cls.query.filter(cls.parent_path.any(node_id)).all()
        else:
            nodes = cls.query.filter(cls.parent_path.any(node_id)).all()
            for node in nodes:
                node.theory_exists = node.theory is not None
                del node.theory
            return nodes

    @classmethod
    def get_all_nodes_of_tree(cls, get_theory=True):
        """
        Returns a list of nodes of the tree
        :param get_theory: if True return theory also else provide a boolean `theory_exists`
        :return: a list
        """
        if get_theory:
            return cls.query.filter().all()
        else:
            nodes = cls.query.filter().all()
            for node in nodes:
                node.theory_exists = node.theory is not None
                del node.theory
            return nodes

    @property
    def absolute_path(self):
        """
        Returns the absolute path of an ontology node

        :return: a list of node_ids
        """
        path = self.parent_path[:]
        path.append(self.id)
        return path
Exemplo n.º 21
0
class DataOperator(User, db.Model):
    __tablename__ = 'data_operators'
    type = db.Column(db.Integer,
                     db.ForeignKey('user_types.id'),
                     nullable=False)

    @classmethod
    def get_list(cls, page=1, limit=10):
        """
        Get a list of active data operators with their stats

        :param page:
        :param limit:
        :return:
        """
        result = {}

        pag_obj = cls.query.filter_by(is_active=True).paginate(page, limit)
        data_operators = pag_obj.items
        total = pag_obj.total
        if total == 0:
            return [], 0

        for op in data_operators:
            result[op.id] = {'data_operator': op}
        data_operator_type = data_operators[0].type
        data_operator_ids = result.keys()

        ques_counts = db.session.query(
            Question.created_by_id, func.count(Question.id)).filter(
                Question.created_by_type == data_operator_type,
                Question.created_by_id.in_(data_operator_ids)).group_by(
                    Question.created_by_id)
        for id, count in ques_counts:
            result[id]['questions_added'] = count

        text_sol_counts = db.session.query(Question.text_solution_by_id, func.count(Question.id)).filter(
            Question.text_solution_by_type == data_operator_type, Question.text_solution_by_id.in_(data_operator_ids))\
            .group_by(Question.text_solution_by_id)
        for id, count in text_sol_counts:
            result[id]['text_solutions_added'] = count

        video_sol_counts = db.session.query(Question.video_solution_by_id, func.count(Question.id)).filter(
            Question.video_solution_by_type == data_operator_type, Question.video_solution_by_id.in_(data_operator_ids))\
            .group_by(Question.video_solution_by_id)
        for id, count in video_sol_counts:
            result[id]['video_solutions_added'] = count

        return result.values(), total

    @classmethod
    def get(cls, id):
        """
        Get a single data operator with his stats

        :param id:
        :return:
        """

        data_operator = cls.query.get(id)
        if data_operator is None:
            raise InvalidDataOperatorId

        ques_count = Question.query.filter(
            Question.created_by_type == data_operator.type,
            Question.created_by_id == data_operator.id).count()
        text_sol_count = Question.query.filter(
            Question.text_solution_by_type == data_operator.type,
            Question.text_solution_by_id == data_operator.id).count()
        video_sol_count = Question.query.filter(
            Question.video_solution_by_type == data_operator.type,
            Question.video_solution_by_id == data_operator.id).count()

        return {
            'data_operator': data_operator,
            'questions_added': ques_count,
            'text_solutions_added': text_sol_count,
            'video_solutions_added': video_sol_count
        }

    @classmethod
    def create(cls, name, email, password):
        """
        Create a data operator. If need encrypted password, then provide one. No encryption is done in this function.

        :param name:
        :param email:
        :param password: encrypted/unencrypted password
        :return:
        """
        user_type = UserTypes.query.filter_by(name='data_operator').first()
        data_operator = cls(name=name,
                            email=email,
                            password=password,
                            type=user_type.id)
        db.session.add(data_operator)
        try:
            db.session.commit()
        except IntegrityError:
            db.session.rolback()
            raise EmailAlreadyRegistered
        return data_operator
Exemplo n.º 22
0
class Institute(User, db.Model):
    __tablename__ = 'institutes'
    type = db.Column(db.Integer,
                     db.ForeignKey('user_types.id'),
                     nullable=False)
    location = db.Column(db.String(100))
    logo_url = db.Column(db.String(app.config['URL_MAX_LENGTH']))
    username = db.Column(db.String(app.config['USERNAME_MAX_LENGTH']),
                         unique=True)
    mobile_no = db.Column(db.String(app.config['MOBILE_NO_MAX_LENGTH']),
                          unique=True)
    batches = db.relationship('Batch', backref='institute', lazy='dynamic')
    fp_token = db.Column(db.String(64))

    @classmethod
    def authenticate_by_username(cls, username, password):
        """
        Used in institute login

        :param username: chosen username of the institute
        :param password:
        :return: the institute row if authentication successful, none otherwise
        """
        with app.app_context():
            ins = cls.query.filter(cls.username == username,
                                   cls.password == password).first()
            return ins

    @classmethod
    def create(cls,
               name,
               email,
               password,
               username,
               location=None,
               mobile_no=None,
               logo_url=None):
        """
        Create an institute

        :param name:
        :param email:
        :param password:
        :param username:
        :param location:
        :param mobile_no:
        :param logo_url:
        :return:
        """
        user_type = UserTypes.query.filter_by(name='teacher').first()
        institute = cls(name=name,
                        email=email,
                        password=password,
                        username=username,
                        location=location,
                        mobile_no=mobile_no,
                        logo_url=logo_url,
                        type=user_type.id)
        db.session.add(institute)
        try:
            db.session.commit()
        except IntegrityError as e:
            db.session.rollback()
            if 'email' in e.message:
                raise EmailAlreadyRegistered
            if 'username' in e.message:
                raise UsernameAlreadyRegistered
            if 'mobile_no' in e.message:
                raise MobileNoAlreadyRegistered
            raise e
        return institute

    @classmethod
    def get(cls, id):
        """
        Get details of a single institute

        :param id: institute id
        :return: institute object
        """
        institute = cls.query.get(id)
        if institute is not None:
            return institute
        else:
            raise InvalidInstituteId

    @classmethod
    def get_list(cls, page=1, limit=10):
        """
        Get a list of institutes

        :param page:
        :param limit:
        :return:
        """
        institutes_pag_obj = cls.query.filter_by(is_active=True).paginate(
            page, limit)
        institutes = institutes_pag_obj.items
        total = institutes_pag_obj.total

        return institutes, total