예제 #1
0
def _copy_facts_to_subscribers(facts, subscribers):
    '''
    The meat-and-potatoes of the copy operation.
    '''
    from manabi.apps.flashcards.models import Card, Fact

    shared_deck = facts[0].deck
    subscriber_decks = shared_deck.subscriber_decks.filter(
        owner__in=subscribers,
        active=True,
    )
    subscriber_deck_values = subscriber_decks.values_list('id', 'owner_id')
    subscriber_decks_already_with_facts = (
        _subscriber_decks_already_with_facts(subscriber_decks, facts))

    fact_cards_prefetch = Prefetch(
        'card_set',
        queryset=Card.objects.filter(active=True, suspended=False),
        to_attr='available_cards',
    )
    try:
        facts = (facts.filter(
            active=True).prefetch_related(fact_cards_prefetch))
    except AttributeError:
        facts = [fact for fact in facts if fact.active]
        prefetch_related_objects(facts, fact_cards_prefetch)

    copied_facts = []
    copied_cards = []
    for shared_fact in facts:
        copy_attrs = [
            'active',
            'suspended',
            'new_fact_ordinal',
            'expression',
            'reading',
            'meaning',
        ]
        fact_kwargs = {attr: getattr(shared_fact, attr) for attr in copy_attrs}

        for subscriber_deck_id, subscriber_id in subscriber_deck_values:
            if _subscriber_deck_already_has_fact(
                    subscriber_deck_id,
                    shared_fact,
                    subscriber_decks_already_with_facts,
            ):
                continue

            fact = Fact(deck_id=subscriber_deck_id,
                        synchronized_with=shared_fact,
                        **fact_kwargs)
            copied_facts.append(fact)

            # Copy the cards.
            copied_cards_for_fact = []
            for shared_card in shared_fact.available_cards:
                card = shared_card.copy(fact, owner_id=subscriber_id)
                copied_cards_for_fact.append(card)
            copied_cards.append(copied_cards_for_fact)

    # Persist everything.
    created_facts = Fact.objects.bulk_create(copied_facts,
                                             batch_size=BULK_BATCH_SIZE)
    for fact, fact_cards in itertools.izip(created_facts, copied_cards):
        for fact_card in fact_cards:
            fact_card.fact_id = fact.id
    Card.objects.bulk_create(itertools.chain.from_iterable(copied_cards),
                             batch_size=BULK_BATCH_SIZE)
예제 #2
0
def rest_fact(request, fact_id):  #todo:refactor into facts
    if request.method == 'POST':
        # Update fact

        # Override the submitted deck ID with the ID from the URL.
        post_data = request.POST.copy()

        #todo: refactor this into model code

        # if this fact is a shared fact which the current subscribing user
        # hasn't copied yet, copy it first
        fact = Fact.objects.get_for_owner_or_subscriber(fact_id, request.user)

        #fact_form = FactForm(post_data, prefix='fact', instance=fact)
        FactFormset = modelformset_factory(Fact,
                                           fields=(
                                               'id',
                                               'fact_type',
                                           ),
                                           can_delete=True)
        fact_formset = FactFormset(post_data,
                                   prefix='fact',
                                   queryset=Fact.objects.filter(id=fact.id)
                                   | fact.subfacts)

        #TODO-OLD make from CardForm
        CardFormset = modelformset_factory(Card,
                                           exclude=(
                                               'fact',
                                               'ease_factor',
                                           ))
        card_formset = CardFormset(post_data,
                                   prefix='card',
                                   queryset=fact.card_set.get_query_set())

        FieldContentFormset = modelformset_factory(FieldContent,
                                                   form=FieldContentForm)
        field_content_queryset = (fact.fieldcontent_set.get_query_set()
                                  or None)
        field_content_formset = FieldContentFormset(post_data,
                                                    prefix='field_content')
        #, queryset=field_content_queryset)

        #fact_form = FactForm(post_data, prefix='fact', instance=fact)
        # ^^^^^^^ this isn't updated
        if (card_formset.is_valid() and field_content_formset.is_valid()
                and fact_formset.is_valid()):
            #fact = fact_form.save() #TODO-OLD needed in future?

            #update the fact's assigned deck
            #FIXME catch error if does not exist
            #deck_id = int(post_data['fact-deck'])
            #fact.deck = Deck.objects.get(id=deck_id)
            #fact.save()

            # maps subfact group numbers to the subfact object
            group_to_subfact = {}
            for field_content_form in field_content_formset.forms:
                field_content = field_content_form.save(commit=False)

                # is this a field of the parent fact, or a subfact?
                if field_content.field_type.fact_type == fact.fact_type:
                    # Parent fact.
                    field_content.fact = fact
                    field_content.save()
                else:
                    # Subfact.
                    # Does this subfact already belong to the user?
                    # If not, create it, only if anything's changed.
                    # Or, create it, if it's new.
                    if field_content_form.cleaned_data['id']:
                        # existing field content

                        # if it's part of a subfact that's being
                        # deleted in this form, ignore the field.
                        if field_content_form.cleaned_data['id'].fact in\
                            [fact_form.cleaned_data['id'] for fact_form
                             in fact_formset.deleted_forms]:
                            continue

                        if (field_content_form.cleaned_data['id'].fact.owner ==
                                request.user):
                            #TODO-OLD is this necessary? vvv
                            field_content.fact = \
                                    field_content_form.cleaned_data['id'].fact
                            field_content.save()
                        else:
                            original = field_content_form.cleaned_data['id']
                            if (field_content_form['content'] !=
                                    original.content):
                                # user updated subscribed subfact content
                                # - so create his own subscriber subfact to
                                # hold it.
                                new_subfact = original.fact.copy_to_parent_fact(
                                    fact, copy_field_contents=True)
                                new_field_content = new_subfact.fieldcontent_set.get(
                                    field_type=field_content_form.
                                    cleaned_data['field_type'])
                                new_field_content.content = \
                                        field_content_form.cleaned_data['content']
                                new_field_content.save()
                            else:
                                # not user's own, but he didn't update it anyway
                                pass
                    else:
                        # new field content
                        # this means new subfact.
                        # otherwise, this doesn't make sense unless the subfact
                        # model changed - which isn't supported yet.
                        # or subscriber fields are optimized to not copy over
                        # until modified
                        group = field_content_form.cleaned_data[
                            'subfact_group']
                        if group not in group_to_subfact.keys():
                            # create the new subfact
                            new_subfact = Fact(
                                fact_type=field_content.field_type.fact_type,
                                active=True,
                                parent_fact=fact)
                            new_subfact.save()
                            group_to_subfact[group] = new_subfact
                        field_content.fact = group_to_subfact[group]
                        field_content.save()

            # delete any subfacts as needed
            for subfact_form in fact_formset.deleted_forms:
                subfact = subfact_form.cleaned_data['id']
                # make sure it's a subfact
                if subfact.parent_fact:  # == fact:
                    if subfact.synchronized_with or subfact.parent_fact != fact:
                        # this is a subscriber fact
                        if subfact.synchronized_with:
                            subfact.active = False
                            subfact.save()
                        else:
                            # the user doesn't have his own copy of this
                            # subfact yet
                            new_subfact = subfact.copy_to_parent_fact(
                                fact, copy_field_contents=False)
                            new_subfact.active = False
                            new_subfact.save()
                    else:
                        subfact.delete()

            # disable any existing cards that weren't selected in the update,
            # or enable if selected and create if needed
            # do all this for subscribers too, if this is in a shared deck
            facts = Fact.objects.filter(id=fact.id)
            if fact.subscriber_facts.all():
                facts = facts | fact.subscriber_facts.all()
            for fact2 in facts.iterator():
                card_form_template_ids = dict(
                    (card_form.cleaned_data['template'].id, card_form)
                    for card_form in card_formset.forms)
                for card_template in fact.fact_type.cardtemplate_set.all():
                    if card_template.id in card_form_template_ids.keys():
                        try:
                            card = fact2.card_set.get(template=card_template)
                            card.activate()
                        except Card.DoesNotExist:
                            #card_form = card_form_template_ids
                            #[card_template.id]
                            #new_card = card_form.save(commit=False)
                            new_card = Card(template=card_template)
                            new_card.fact = fact2
                            new_card.active = True
                            new_card.randomize_new_order()
                            new_card.save()
                    else:
                        #card was not selected in update, so disable it
                        # if it exists
                        try:
                            card = fact2.card_set.get(template=card_template)
                            if not card.active:
                                continue
                            elif fact2.synchronized_with and card.review_count:
                                # don't disable subscriber cards which have
                                # already been reviewed
                                continue
                            card.deactivate()
                        except Card.DoesNotExist:
                            pass
        else:
            raise ApiException({
                'card': card_formset.errors,
                'fact': fact_formset.errors,
                'field_content': field_content_formset.errors,
            })
    elif request.method == 'DELETE':
        fact = Fact.objects.get_for_owner_or_subscriber(fact_id, request.user)
        deleted_fact = fact.delete_for_user(request.user)
        fact_deleted.send(deleted_fact)
예제 #3
0
def rest_fact(request, fact_id): #todo:refactor into facts
    if request.method == 'POST':
        # Update fact

        # Override the submitted deck ID with the ID from the URL.
        post_data = request.POST.copy()

        #todo: refactor this into model code

        # if this fact is a shared fact which the current subscribing user
        # hasn't copied yet, copy it first
        fact = Fact.objects.get_for_owner_or_subscriber(fact_id, request.user)

        #fact_form = FactForm(post_data, prefix='fact', instance=fact)
        FactFormset = modelformset_factory(
                Fact, fields=('id', 'fact_type',), can_delete=True)
        fact_formset = FactFormset(
                post_data, prefix='fact',
                queryset=Fact.objects.filter(id=fact.id)|fact.subfacts)

        #TODO-OLD make from CardForm
        CardFormset = modelformset_factory(
                Card, exclude=('fact', 'ease_factor', ))
        card_formset = CardFormset(
                post_data, prefix='card',
                queryset=fact.card_set.get_query_set())

        FieldContentFormset = modelformset_factory(
                FieldContent, form=FieldContentForm)
        field_content_queryset = (fact.fieldcontent_set.get_query_set()
                                  or None)
        field_content_formset = FieldContentFormset(
                post_data, prefix='field_content')
                #, queryset=field_content_queryset)

        #fact_form = FactForm(post_data, prefix='fact', instance=fact)
        # ^^^^^^^ this isn't updated
        if (card_formset.is_valid()
                and field_content_formset.is_valid()
                and fact_formset.is_valid()):
            #fact = fact_form.save() #TODO-OLD needed in future?

            #update the fact's assigned deck
            #FIXME catch error if does not exist
            #deck_id = int(post_data['fact-deck'])
            #fact.deck = Deck.objects.get(id=deck_id)
            #fact.save()

            # maps subfact group numbers to the subfact object
            group_to_subfact = {}
            for field_content_form in field_content_formset.forms:
                field_content = field_content_form.save(commit=False)

                # is this a field of the parent fact, or a subfact?
                if field_content.field_type.fact_type == fact.fact_type:
                    # Parent fact.
                    field_content.fact = fact
                    field_content.save()
                else:
                    # Subfact.
                    # Does this subfact already belong to the user?
                    # If not, create it, only if anything's changed.
                    # Or, create it, if it's new.
                    if field_content_form.cleaned_data['id']:
                        # existing field content

                        # if it's part of a subfact that's being
                        # deleted in this form, ignore the field.
                        if field_content_form.cleaned_data['id'].fact in\
                            [fact_form.cleaned_data['id'] for fact_form
                             in fact_formset.deleted_forms]:
                            continue

                        if (field_content_form.cleaned_data['id'].fact.owner
                                == request.user):
                            #TODO-OLD is this necessary? vvv
                            field_content.fact = \
                                    field_content_form.cleaned_data['id'].fact
                            field_content.save()
                        else:
                            original = field_content_form.cleaned_data['id']
                            if (field_content_form['content']
                                    != original.content):
                                # user updated subscribed subfact content
                                # - so create his own subscriber subfact to
                                # hold it.
                                new_subfact = original.fact.copy_to_parent_fact(
                                        fact, copy_field_contents=True)
                                new_field_content = new_subfact.fieldcontent_set.get(
                                        field_type=field_content_form.cleaned_data['field_type'])
                                new_field_content.content = \
                                        field_content_form.cleaned_data['content']
                                new_field_content.save()
                            else:
                                # not user's own, but he didn't update it anyway
                                pass
                    else:
                        # new field content
                        # this means new subfact.
                        # otherwise, this doesn't make sense unless the subfact
                        # model changed - which isn't supported yet.
                        # or subscriber fields are optimized to not copy over
                        # until modified
                        group = field_content_form.cleaned_data['subfact_group']
                        if group not in group_to_subfact.keys():
                            # create the new subfact
                            new_subfact = Fact(
                                fact_type=field_content.field_type.fact_type,
                                active=True,
                                parent_fact=fact
                            )
                            new_subfact.save()
                            group_to_subfact[group] = new_subfact
                        field_content.fact = group_to_subfact[group]
                        field_content.save()


            # delete any subfacts as needed
            for subfact_form in fact_formset.deleted_forms:
                subfact = subfact_form.cleaned_data['id']
                # make sure it's a subfact
                if subfact.parent_fact:# == fact:
                    if subfact.synchronized_with or subfact.parent_fact != fact:
                        # this is a subscriber fact
                        if subfact.synchronized_with:
                            subfact.active = False
                            subfact.save()
                        else:
                            # the user doesn't have his own copy of this
                            # subfact yet
                            new_subfact = subfact.copy_to_parent_fact(
                                    fact, copy_field_contents=False)
                            new_subfact.active = False
                            new_subfact.save()
                    else:
                        subfact.delete()


            # disable any existing cards that weren't selected in the update,
            # or enable if selected and create if needed
            # do all this for subscribers too, if this is in a shared deck
            facts = Fact.objects.filter(id=fact.id)
            if fact.subscriber_facts.all():
                facts = facts | fact.subscriber_facts.all()
            for fact2 in facts.iterator():
                card_form_template_ids = dict(
                        (card_form.cleaned_data['template'].id, card_form)
                        for card_form in card_formset.forms)
                for card_template in fact.fact_type.cardtemplate_set.all():
                    if card_template.id in card_form_template_ids.keys():
                        try:
                            card = fact2.card_set.get(template=card_template)
                            card.activate()
                        except Card.DoesNotExist:
                            #card_form = card_form_template_ids
                            #[card_template.id]
                            #new_card = card_form.save(commit=False)
                            new_card = Card(template=card_template)
                            new_card.fact = fact2
                            new_card.active = True
                            new_card.randomize_new_order()
                            new_card.save()
                    else:
                        #card was not selected in update, so disable it
                        # if it exists
                        try:
                            card = fact2.card_set.get(template=card_template)
                            if not card.active:
                                continue
                            elif fact2.synchronized_with and card.review_count:
                                # don't disable subscriber cards which have
                                # already been reviewed
                                continue
                            card.deactivate()
                        except Card.DoesNotExist:
                            pass
        else:
            raise ApiException({
                'card': card_formset.errors,
                'fact': fact_formset.errors,
                'field_content': field_content_formset.errors,
            })
    elif request.method == 'DELETE':
        fact = Fact.objects.get_for_owner_or_subscriber(fact_id, request.user)
        deleted_fact = fact.delete_for_user(request.user)
        fact_deleted.send(deleted_fact)
예제 #4
0
def rest_facts(request, deck=None, tags=None):
    #TODO-OLD refactor into facts (no???)
    if request.method == 'GET':
        ret = []
        if request.GET['fact_type']:
            fact_type_id = request.GET['fact_type']
            fact_type = get_object_or_404(FactType, pk=fact_type_id)

            user = deck.owner if deck else request.user

            facts = Fact.objects.with_upstream(user, deck=deck,
                                               tags=tags).filter(active=True)

            #is the user searching his facts?
            if ('search' in request.GET and request.GET['search'].strip()):
                search_query = request.GET['search']
                facts = Fact.objects.search(fact_type,
                                            search_query,
                                            query_set=facts)
                #FIXME add search for synchronized facts too!

            for fact in facts.iterator():
                row = {
                    'fact-id': fact.id,
                    'suspended': fact.suspended(),
                }

                ident, name = '', ''
                for field_content in fact.field_contents:
                    #TODO-OLD rename to be clearer, like field_id, or ???
                    key = 'id{0}'.format(field_content.field_type_id)

                    if not ident:
                        ident = key
                    elif not name:
                        name = key

                    row[key] = field_content.human_readable_content()
                    row['{0}_field-content-id'.format(key)] = field_content.id

                if not name:
                    name = ident

                ret.append(row)
            ret = to_dojo_data(ret)
            ret['identifier'] = 'fact-id'
            #ret['name'] = name #todo:for <2 cols/fields...?
            return ret
    elif request.method == 'POST':
        # Create fact in deck, including its fields and cards. POST method.
        #TODO-OLD refactor into other module probably
        ret = {}

        #TODO-OLD just get this from the form object.
        deck = get_deck_or_404(request.user,
                               request.POST['fact-deck'],
                               must_own=True)

        # Override the submitted deck ID with the ID from the URL,
        # since this is a RESTful interface.
        post_data = request.POST.copy()
        #post_data['fact-deck'] = deck_id

        #todo: refactor this into model code

        #CardFormset = modelformset_factory(Card, exclude=('fact', 'ease_factor', )) #TODO-OLD make from CardForm
        #card_formset = CardFormset(post_data, prefix='card')
        card_templates = CardTemplate.objects.filter(id__in=[
            e[1] for e in post_data.items() if e[0].find('card_template') == 0
        ])

        #FieldContentFormset = modelformset_factory(FieldContent, exclude=('fact', ))
        FieldContentFormset = modelformset_factory(FieldContent,
                                                   form=FieldContentForm)
        field_content_formset = FieldContentFormset(post_data,
                                                    prefix='field_content')

        fact_form = FactForm(post_data, prefix='fact')

        if field_content_formset.is_valid() and fact_form.is_valid():
            #TODO-OLD automate the tag saving in forms.py
            new_fact = fact_form.save()
            new_fact.active = True
            new_fact.save()

            # maps subfact group numbers to the subfact object
            group_to_subfact = {}
            for field_content_form in field_content_formset.forms:
                #TODO-OLD don't create fieldcontent objects for
                # optional fields which were left blank.
                new_field_content = field_content_form.save(commit=False)
                # is this a field of the parent fact, or a subfact?
                if (new_field_content.field_type.fact_type ==
                        new_fact.fact_type):
                    # parent fact
                    new_field_content.fact = new_fact
                else:
                    # subfact
                    group = field_content_form\
                            .cleaned_data['subfact_group']
                    if group not in group_to_subfact.keys():
                        # create the new subfact
                        new_subfact = Fact(
                                fact_type=new_field_content\
                                          .field_type.fact_type,
                                active=True,
                                #deck=new_fact.deck,
                                parent_fact=new_fact,
                        )
                        new_subfact.save()
                        group_to_subfact[group] = new_subfact
                    new_field_content.fact = group_to_subfact[group]
                new_field_content.save()

            for card_template in card_templates:
                #card_form in card_formset.forms:
                new_card = Card(template=card_template,
                                fact=new_fact,
                                active=True,
                                priority=0)
                new_card.randomize_new_order()
                new_card.save()
        else:
            raise ApiException({
                #'card': card_formset.errors,
                'field_content': field_content_formset.errors,
                'fact': [fact_form.errors]
            })
        return ret
예제 #5
0
def rest_facts(request, deck=None, tags=None):
    #TODO-OLD refactor into facts (no???)
    if request.method == 'GET':
        ret = []
        if request.GET['fact_type']:
            fact_type_id = request.GET['fact_type']
            fact_type = get_object_or_404(FactType, pk=fact_type_id)

            user = deck.owner if deck else request.user

            facts = Fact.objects.with_upstream(
                user, deck=deck, tags=tags).filter(active=True)

            #is the user searching his facts?
            if ('search' in request.GET
                    and request.GET['search'].strip()):
                search_query = request.GET['search']
                facts = Fact.objects.search(
                    fact_type, search_query, query_set=facts)
                #FIXME add search for synchronized facts too!

            for fact in facts.iterator():
                row = {
                    'fact-id': fact.id,
                    'suspended': fact.suspended(),
                }

                ident, name = '', ''
                for field_content in fact.field_contents:
                    #TODO-OLD rename to be clearer, like field_id, or ???
                    key = 'id{0}'.format(field_content.field_type_id)

                    if not ident:
                        ident = key
                    elif not name:
                        name = key

                    row[key] = field_content.human_readable_content()
                    row['{0}_field-content-id'.format(key)] = field_content.id

                if not name:
                    name = ident

                ret.append(row)
            ret = to_dojo_data(ret)
            ret['identifier'] = 'fact-id'
            #ret['name'] = name #todo:for <2 cols/fields...?
            return ret
    elif request.method == 'POST':
        # Create fact in deck, including its fields and cards. POST method.
        #TODO-OLD refactor into other module probably
        ret = {}

        #TODO-OLD just get this from the form object.
        deck = get_deck_or_404(request.user, request.POST['fact-deck'],
                               must_own=True)

        # Override the submitted deck ID with the ID from the URL,
        # since this is a RESTful interface.
        post_data = request.POST.copy()
        #post_data['fact-deck'] = deck_id

        #todo: refactor this into model code

        #CardFormset = modelformset_factory(Card, exclude=('fact', 'ease_factor', )) #TODO-OLD make from CardForm
        #card_formset = CardFormset(post_data, prefix='card')
        card_templates = CardTemplate.objects.filter(
                id__in=[e[1] for e in post_data.items()
                if e[0].find('card_template') == 0])

        #FieldContentFormset = modelformset_factory(FieldContent, exclude=('fact', ))
        FieldContentFormset = modelformset_factory(
            FieldContent, form=FieldContentForm)
        field_content_formset = FieldContentFormset(
            post_data, prefix='field_content')

        fact_form = FactForm(post_data, prefix='fact')

        if field_content_formset.is_valid() and fact_form.is_valid():
            #TODO-OLD automate the tag saving in forms.py
            new_fact = fact_form.save()
            new_fact.active = True
            new_fact.save()

            # maps subfact group numbers to the subfact object
            group_to_subfact = {}
            for field_content_form in field_content_formset.forms:
                #TODO-OLD don't create fieldcontent objects for
                # optional fields which were left blank.
                new_field_content = field_content_form.save(commit=False)
                # is this a field of the parent fact, or a subfact?
                if (new_field_content.field_type.fact_type
                    == new_fact.fact_type):
                    # parent fact
                    new_field_content.fact = new_fact
                else:
                    # subfact
                    group = field_content_form\
                            .cleaned_data['subfact_group']
                    if group not in group_to_subfact.keys():
                        # create the new subfact
                        new_subfact = Fact(
                                fact_type=new_field_content\
                                          .field_type.fact_type,
                                active=True,
                                #deck=new_fact.deck,
                                parent_fact=new_fact,
                        )
                        new_subfact.save()
                        group_to_subfact[group] = new_subfact
                    new_field_content.fact = group_to_subfact[group]
                new_field_content.save()

            for card_template in card_templates:
                #card_form in card_formset.forms:
                new_card = Card(
                    template=card_template,
                    fact=new_fact,
                    active=True,
                    priority = 0)
                new_card.randomize_new_order()
                new_card.save()
        else:
            raise ApiException({
                #'card': card_formset.errors,
                'field_content': field_content_formset.errors,
                'fact': [fact_form.errors]
            })
        return ret