def set_active_card_templates(self, card_templates): ''' Creates or updates associated `Card`s. ''' from manabi.apps.flashcards.models import Card template_ids = { _card_template_string_to_id(template) for template in card_templates } for activated_card in ( self.card_set.filter(template__in=template_ids) ): activated_card.activate() for deactivated_card in ( self.card_set.exclude(template__in=template_ids) ): deactivated_card.deactivate() existing_template_ids = set(self.card_set.values_list( 'template', flat=True)) for template_id in template_ids - existing_template_ids: Card.objects.create( deck=self.deck, fact=self, template=template_id, new_card_ordinal=Card.random_card_ordinal(), ) self._set_active_card_templates_for_subscribers(template_ids)
def _set_active_card_templates_for_subscribers(self, template_ids): from manabi.apps.flashcards.models import Card subscriber_cards = Card.objects.filter( fact__in=self.syncing_subscriber_facts, ) new_subscriber_cards = subscriber_cards.filter( last_reviewed_at__isnull=True, ) new_subscriber_cards.filter( template__in=template_ids, ).update(active=True) new_subscriber_cards.exclude( template__in=template_ids, ).update(active=False) for template_id in template_ids: facts_without_template = self.syncing_subscriber_facts.exclude( card__in=subscriber_cards.filter(template=template_id), ) missing_cards = [ Card( deck_id=deck_id, fact_id=fact_id, template=template_id, new_card_ordinal=Card.random_card_ordinal(), ) for fact_id, deck_id in facts_without_template.values_list('id', 'deck_id').iterator() ] Card.objects.bulk_create(missing_cards)
def set_active_card_templates(self, card_templates): ''' Creates or updates associated `Card`s. ''' from manabi.apps.flashcards.models import Card template_ids = { _card_template_string_to_id(template) for template in card_templates } for activated_card in (self.card_set.filter( template__in=template_ids)): activated_card.activate() for deactivated_card in (self.card_set.exclude( template__in=template_ids)): deactivated_card.deactivate() existing_template_ids = set( self.card_set.values_list('template', flat=True)) for template_id in template_ids - existing_template_ids: Card.objects.create( deck=self.deck, fact=self, template=template_id, new_card_ordinal=Card.random_card_ordinal(), ) self._set_active_card_templates_for_subscribers(template_ids)
def create_fact(user=None, deck=None): """ Includes card creation. """ deck = deck or create_deck(user=user) fact = Fact.objects.create( deck=deck, expression=random_name(), reading=random_name(), meaning=random_name(), ) for template, template_name in CARD_TEMPLATE_CHOICES: card = Card( deck=deck, fact=fact, template=template, ) card.randomize_new_order() card.save() return fact
def _set_active_card_templates_for_subscribers(self, template_ids): from manabi.apps.flashcards.models import Card subscriber_cards = Card.objects.filter( fact__in=self.syncing_subscriber_facts, ) new_subscriber_cards = subscriber_cards.filter( last_reviewed_at__isnull=True, ) new_subscriber_cards.filter( template__in=template_ids, ).update(active=True) new_subscriber_cards.exclude( template__in=template_ids, ).update(active=False) for template_id in template_ids: facts_without_template = self.syncing_subscriber_facts.exclude( card__in=subscriber_cards.filter( template=template_id), ).select_related('deck') missing_cards = [ Card( owner_id=owner_id, deck_id=deck_id, deck_suspended=deck_suspended, fact_id=fact_id, template=template_id, new_card_ordinal=Card.random_card_ordinal(), ) for fact_id, deck_id, deck_suspended, owner_id in facts_without_template.values_list( 'id', 'deck_id', 'deck__suspended', 'deck__owner_id', ).iterator() ] Card.objects.bulk_create(missing_cards)
def create_fact(user=None, deck=None): """ Includes card creation. """ deck = deck or create_deck(user=user) fact = Fact.objects.create( deck=deck, expression=random_name(), reading=random_name(), meaning=random_name(), ) for template, template_name in CARD_TEMPLATE_CHOICES: card = Card( deck=deck, owner_id=deck.owner_id, fact=fact, template=template, ) card.randomize_new_order() card.save() return fact
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)
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
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)
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
name='rest-deck_list'), url(r'^shared-decks/$', SharedDeckList.as_view(), name='rest-shared_deck_list'), url(r'^decks/(?P<pk>\d+)/subscriptions/$', DeckSubscription.as_view(), name='rest-deck_subscription'), url(r'^decks/(?P<pk>\d+)/status/$', DeckStatus.as_view(), name='rest-deck_status'), url(r'^decks/(?P<pk>\d+)/$', Deck.as_view(), name='rest-deck'), url(r'^decks/all/$', AllDecks.as_view(), name='rest-all_decks'), url(r'^next-cards-for-review/$', NextCardsForReview.as_view(), name='rest-next_cards_for_review'), url(r'^next-cards-for-review/undo-stack/$', ReviewUndo.as_view(), name='rest-review_undo_stack'), url(r'^cards/(?P<pk>\d+)/reviews/$', CardReviews.as_view(), name='rest-card_reviews'), url(r'^cards/(?P<pk>\d+)/$', Card.as_view(), name='rest-card'), ) urlpatterns += patterns('', url(r'^internal-api/', include(internal_api_urlpatterns)), )