def article_recommendations_for_user(user, count):
    """

            Retrieve :param count articles which are equally distributed
            over all the feeds to which the :param user is registered to.

            Fails if no language is selected.

    :return:

    """

    import zeeguu_core

    user_languages = UserLanguage.all_reading_for_user(user)
    if not user_languages:
        return []

    reading_pref_hash = reading_preferences_hash(user)
    recompute_recommender_cache_if_needed(user, zeeguu_core.db.session)
    all_articles = ArticlesCache.get_articles_for_hash(reading_pref_hash,
                                                       count)
    all_articles = [
        each for each in all_articles
        if (not each.broken and each.published_time)
    ]
    all_articles = SortedList(all_articles, lambda x: x.published_time)

    return [
        user_article_info(user, article) for article in reversed(all_articles)
    ]
def filter_subscribed_articles(subscribed_articles, user_languages, user):
    """
    :param subscribed_articles:
    :param user_filters:
    :param user_languages:
    :param user_search_filters:
    :return:

            a generator which retrieves articles as needed

    """

    def _article_matches_user_topic_filters(article, filters):
        return not set(article.topics).isdisjoint([each.topic for each in filters])

    user_search_filters = SearchFilter.all_for_user(user)

    user_filters = TopicFilter.all_for_user(user)

    keywords_to_avoid = []
    for user_search_filter in user_search_filters:
        keywords_to_avoid.append(user_search_filter.search.keywords)

    subscribed_articles = (art for art in subscribed_articles if
                           (art.language in user_languages)
                           and not art.broken
                           and (UserLanguage.appropriate_level(art, user))
                           and not _article_matches_user_topic_filters(art, user_filters)
                           and not (art.contains_any_of(keywords_to_avoid)))
    return subscribed_articles
def reading_preferences_hash(user):
    """

            Method to retrieve the hash, as this is done several times.

    :param user:
    :return: articles_hash: ArticlesHash

    """
    user_filter_subscriptions = TopicFilter.all_for_user(user)
    filters = [topic_id.topic for topic_id in user_filter_subscriptions]

    user_topic_subscriptions = TopicSubscription.all_for_user(user)
    topics = [topic_id.topic for topic_id in user_topic_subscriptions]

    user_languages = UserLanguage.all_user_languages__reading_for_user(user)

    user_search_filters = SearchFilter.all_for_user(user)

    search_filters = [search_id.search for search_id in user_search_filters]
    user_searches = SearchSubscription.all_for_user(user)

    searches = [search_id.search for search_id in user_searches]

    articles_hash = ArticlesCache.calculate_hash(topics, filters, searches,
                                                 search_filters,
                                                 user_languages)

    return articles_hash
Example #4
0
    def levels_for(self, language: Language):
        """

            the level that the system considers for this user

            TODO: must think better about this...

        :param language:

        :return: pair of level_min and level_max for this user

        """
        from zeeguu_core.model import UserLanguage

        lang_info = UserLanguage.with_language_id(language.id, self)

        # default values, for when there's no corresponding setting
        declared_level_min = -1
        declared_level_max = 11

        # start from user's levels if they exist
        if lang_info.declared_level_min:
            if lang_info.declared_level_min > 0:
                declared_level_min = lang_info.declared_level_min

        if lang_info.declared_level_max:
            if lang_info.declared_level_max < 10:
                declared_level_max = lang_info.declared_level_max

        CEFR_TO_DIFFICULTY_MAPPING = {
            1: (1, 5.5),
            2: (1, 6),
            3: (3, 7),
            4: (4, 8),
            5: (6, 9),
            6: (7, 10)
        }

        if lang_info.cefr_level and lang_info.cefr_level > 0:
            declared_level_min, declared_level_max = CEFR_TO_DIFFICULTY_MAPPING[
                lang_info.cefr_level]

        # If there's cohort info, consider it
        if self.cohort:
            if self.cohort.language:
                if self.cohort.language == language:
                    if self.cohort.declared_level_min:
                        # min will be the max between the teacher's min and the student's min
                        # this means that if the teacher says 5 is min, the student can't reduce it...
                        # otoh, if the teacher says 5 is the min but the student wants 7 that will work
                        declared_level_min = max(
                            declared_level_min, self.cohort.declared_level_min)

                    if self.cohort.declared_level_max:
                        # a student is limited to the upper limit of his cohort
                        declared_level_max = min(
                            declared_level_max, self.cohort.declared_level_max)

        return max(declared_level_min, 0), min(declared_level_max, 10)
Example #5
0
    def set_learned_language_level(self, language_code: str, level: str, session=None):
        learned_language = Language.find_or_create(language_code)
        from zeeguu_core.model import UserLanguage

        language = UserLanguage.find_or_create(session, self, learned_language)
        language.cefr_level = int(level)
        if session:
            session.add(language)
Example #6
0
    def set_learned_language(self, language_code, session=None):
        self.learned_language = Language.find(language_code)

        from zeeguu_core.model import UserLanguage

        # disable the exercises and reading for all the other languages
        all_other_languages = (UserLanguage.query.filter(
            User.id == self.id).filter(
                UserLanguage.doing_exercises == True).all())
        for each in all_other_languages:
            each.doing_exercises = False
            each.reading_news = False
            if session:
                session.add(each)

        language = UserLanguage.find_or_create(session, self,
                                               self.learned_language)
        language.reading_news = True
        language.doing_exercises = True

        if session:
            session.add(language)
def find_articles_for_user(user):
    """

    This method gets all the topic and search subscriptions for a user.
    It then returns all the articles that are associated with these.

    :param user:
    :return:

    """

    user_languages = UserLanguage.all_reading_for_user(user)

    topic_subscriptions = TopicSubscription.all_for_user(user)

    search_subscriptions = SearchSubscription.all_for_user(user)

    subscribed_articles = filter_subscribed_articles(search_subscriptions,
                                                     topic_subscriptions,
                                                     user_languages, user)

    return subscribed_articles
Example #8
0
 def __init__(self,
              email,
              name,
              password,
              learned_language=None,
              native_language=None,
              invitation_code=None,
              cohort=None):
     self.email = email
     self.name = name
     self.update_password(password)
     self.learned_language = learned_language or Language.default_learned()
     self.native_language = native_language or Language.default_native_language(
     )
     self.invitation_code = invitation_code
     self.cohort = cohort
     # Add the learned language to user languages and set reading_news to True
     # so that the user has articles in the reader when opening it for the first time.
     from zeeguu_core.model import UserLanguage
     UserLanguage(self,
                  learned_language or Language.default_learned(),
                  reading_news=True)
def get_user_articles_sources_languages(user, limit=1000):
    """

    This method is used to get all the user articles for the sources if there are any
    selected sources for the user, and it otherwise gets all the articles for the
    current learning languages for the user.

    :param user: the user for which the articles should be fetched
    :param limit: the amount of articles for each source or language
    :return: a list of articles based on the parameters

    """

    user_languages = UserLanguage.all_reading_for_user(user)
    all_articles = []

    for language in user_languages:
        log(f'Getting articles for {language}')
        new_articles = language.get_articles(most_recent_first=True)
        all_articles.extend(new_articles)
        log(f'Added {len(new_articles)} articles for {language}')

    return all_articles
all_users = User.query.all()

for user in all_users:
    learned_languages = []

    # Grab the learned language and add it to learned_languages
    learned_languages.append(user.learned_language)

    # Go through the user sources
    user_feed_registrations = RSSFeedRegistration.feeds_for_user(user)

    for feed_reg in user_feed_registrations:
        # Try catch is needed because apparently there are NoneType feeds in registrations..
        try:
            feed = feed_reg.rss_feed
            feed_language = feed.language
            if feed_language not in learned_languages:
                learned_languages.append(feed_language)
        except Exception as e:
            print(e)

    for language in learned_languages:
        user_language = UserLanguage.find_or_create(session, user, language)
        user_language.reading_news = True
        session.add(user_language)

    print(f'Added user_languages for user user {user}')
    session.commit()

def create_account(
    db_session,
    username,
    password,
    invite_code,
    email,
    learned_language_code,
    native_language_code,
    learned_cefr_level,
):
    cohort_name = ""
    if password is None or len(password) < 4:
        raise Exception("Password should be at least 4 characters long")

    if not valid_invite_code(invite_code):
        raise Exception(
            "Invitation code is not recognized. Please contact us.")

    cohort = Cohort.query.filter_by(inv_code=invite_code).first()
    if cohort:
        if cohort.cohort_still_has_capacity():
            cohort_name = cohort.name
        else:
            raise Exception(
                "No more places in this class. Please contact us ([email protected])."
            )

    try:

        learned_language = Language.find_or_create(learned_language_code)
        native_language = Language.find_or_create(native_language_code)

        new_user = User(
            email,
            username,
            password,
            invitation_code=invite_code,
            cohort=cohort,
            learned_language=learned_language,
            native_language=native_language,
        )

        db_session.add(new_user)

        learned_language = UserLanguage.find_or_create(db_session, new_user,
                                                       learned_language)
        learned_language.cefr_level = int(learned_cefr_level)
        # TODO: although these are required... they should simply
        # be functions of CEFR level so at some further point should
        # removed
        learned_language.declared_level_min = 0
        learned_language.declared_level_max = 11

        db_session.add(learned_language)

        if cohort:
            if cohort.is_cohort_of_teachers:
                teacher = Teacher(new_user)
                db_session.add(teacher)

        db_session.commit()

        send_new_user_account_email(username, invite_code, cohort_name)

        return new_user

    except sqlalchemy.exc.IntegrityError:
        raise Exception("There is already an account for this email.")
    except Exception as e:
        print(e)
        raise Exception("Could not create the account")