docs: def practice(request):
      """
      Return the given number of questions to practice adaptively. In case of
      POST request, try to save the answer(s).
  
      GET parameters:
          filter:
              list of lists of identifiers (may be prefixed by minus sign to
              mark complement)
          language:
              language (str) of items
          avoid:
              list of item ids to avoid
          limit:
              number of returned questions (default 10, maximum 100)
          time:
              time in format '%Y-%m-%d_%H:%M:%S' used for practicing
          user:
              identifier for the practicing user (only for stuff users)
          stats:
              turn on the enrichment of the objects by some statistics
          html:
              turn on the HTML version of the API
  
      BODY:
          see answer resource
      """
      if request.user.id is None:  # Google Bot
          return render_json(request, {
              'error': _('There is no user available for the practice.'),
              'error_type': 'user_undefined'
          }, status=400, template='models_json.html')
  
      limit = min(int(request.GET.get('limit', 10)), 100)
      # prepare
      user = get_user_id(request)
      time = get_time(request)
      avoid = load_query_json(request.GET, "avoid", "[]")
      practice_filter = get_filter(request)
      practice_context = PracticeContext.objects.from_content(practice_filter)
      environment = get_environment()
      item_selector = get_item_selector()
      if is_time_overridden(request):
          environment.shift_time(time)
  
      # save answers
      if request.method == 'POST':
          _save_answers(request, practice_context, False)
      elif request.method == 'GET':
          PracticeSet.objects.filter(answer__user_id=request.user.id).update(finished=True)
  
      if limit > 0:
          item_ids = Item.objects.filter_all_reachable_leaves(practice_filter, get_language(request), forbidden_identifiers=get_forbidden_items())
          item_ids = list(set(item_ids) - set(avoid))
          limit_size = get_config('proso_models', 'practice.limit_item_set_size_to_select_from', default=None)
          if limit_size is not None and limit_size < len(item_ids):
              item_ids = sample(item_ids, limit_size)
          if len(item_ids) == 0:
              return render_json(request, {
                  'error': _('There is no item for the given filter to practice.'),
                  'error_type': 'empty_practice'
              }, status=404, template='models_json.html')
          selected_items, meta = item_selector.select(environment, user, item_ids, time, practice_context.id, limit, items_in_queue=len(avoid))
          result = []
          for item, item_meta in zip(selected_items, meta):
              question = {
                  'object_type': 'question',
                  'payload': Item.objects.item_id_to_json(item),
              }
              if item_meta is not None:
                  question['meta'] = item_meta
              result.append(question)
      else:
          result = []
  
      return render_json(request, result, template='models_json.html', help_text=practice.__doc__)