app/soc/models/question.py
changeset 339 b9be44e09530
child 342 72482d8e5b34
equal deleted inserted replaced
338:0d78f41dde9b 339:b9be44e09530
       
     1 #!/usr/bin/python2.5
       
     2 #
       
     3 # Copyright 2008 the Melange authors.
       
     4 #
       
     5 # Licensed under the Apache License, Version 2.0 (the "License");
       
     6 # you may not use this file except in compliance with the License.
       
     7 # You may obtain a copy of the License at
       
     8 # 
       
     9 #   http://www.apache.org/licenses/LICENSE-2.0
       
    10 # 
       
    11 # Unless required by applicable law or agreed to in writing, software
       
    12 # distributed under the License is distributed on an "AS IS" BASIS,
       
    13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    14 # See the License for the specific language governing permissions and
       
    15 # limitations under the License.
       
    16 
       
    17 """This module contains the Question Model."""
       
    18 
       
    19 __authors__ = [
       
    20   '"Todd Larsen" <tlarsen@google.com>',
       
    21 ]
       
    22 
       
    23 
       
    24 from google.appengine.ext import db
       
    25 
       
    26 import soc.models.work
       
    27 
       
    28 
       
    29 class Question(soc.models.work.Work):
       
    30   """Model of a Question, which is a specialized form of Work.
       
    31 
       
    32   Specific types of Questions are actually implemented in subclasses.
       
    33 
       
    34   The specific way that the properties and relations inherited from
       
    35   Work are used with a Question are described below.
       
    36 
       
    37     work.title:  the title of the Question, used for finding the
       
    38       Question in a list of Questions
       
    39 
       
    40     work.abstract:  the Question text, asked to the respondent
       
    41 
       
    42     work.authors:  the Authors of the Work referred to by this relation
       
    43       are the authors of the Question
       
    44 
       
    45     work.reviews:  even Questions can be "reviewed" (possibly commented
       
    46       on during creation or annotated once put into use).
       
    47 
       
    48     work.partial_path: used to scope (and, when combined with
       
    49       work.link_name, uniquely identify) a Question in the same way the
       
    50       property are used with Documents, etc.
       
    51 
       
    52     work.link_name: used to identify (and, when combined with
       
    53       work.partial_path, *uniquely* identify) a Question in the same way
       
    54       these properties are used with Documents, etc.
       
    55       
       
    56   In addition to any explicit ReferenceProperties in the Question Model
       
    57   and those inherited as described above, a Question entity participates
       
    58   in these relationships:
       
    59 
       
    60     answers)  a 1:many relationship, where each Question has many different
       
    61       Answers associated with it as parts of Responses to Quizzes.  This is
       
    62       implemented as the 'answers' back-reference Query of the Answer model
       
    63       'question' reference.  It is currently unclear how useful this
       
    64       back-reference will be, since the same Question could be used in
       
    65       multiple different Quizzes. Given this, 'answers' currently only
       
    66       exists for completeness.
       
    67       
       
    68     quizzes)  a many:many relationship between Questions and the Quizzes
       
    69       that collect them into a set.  This relation is not explicitly
       
    70       implemented, but can be obtained via a query something like:
       
    71       
       
    72         quizzes_with_a_question = db.GqlQuery(
       
    73             "SELECT * FROM Quiz where questions = :1",
       
    74             a_question.key())
       
    75 
       
    76       Such queries are probably only needed when a Question might be
       
    77       altered, in order to find which Quizzes will be affected.
       
    78 
       
    79   The properties in this Model do not have verbose_name or help_text,
       
    80   because the dynamic nature of the forms required to create, edit, and
       
    81   use entities of this Model make them pretty useless.
       
    82   
       
    83   ######################################################################
       
    84   # TODO(tlarsen): the following verbose comments can be removed later,
       
    85     when these ideas are implemented in the views and controllers; they
       
    86     are here now so that the concepts will not be lost before that time.
       
    87 
       
    88   The recommended use for the combination of work.partial_path and
       
    89   work.link_name is to keep the *same* link_name when copying and
       
    90   modifying an existing Question for a new Program (or instance of a
       
    91   Group that is per-Program), while changing the work.partial_path to
       
    92   represent the Program and Group "ownership" of the Question.  For
       
    93   example, if a Question asking about prior GSoC participation needed
       
    94   to have an additional choice (see the choice_ids and choices properties
       
    95   below), it is desirable to keep the same work.link_name (and also
       
    96   simply append new choice_ids and choices to keep the old answer values
       
    97   compatible).  An existing Question in the above example might be identified
       
    98   as something like:
       
    99     Question:google/gsoc2009/gsoc_past_participation
       
   100     <type>:<Sponsor>/<Program>/<link_name> 
       
   101   To make it possible to query for gsoc_past_participation answers regardless
       
   102   of the Program, the next year, new values are added to choice_ids and
       
   103   choices in a new Question copied from the one above, which would then
       
   104   be named something (still unique) like:
       
   105     Question:google/gsoc2010/gsoc_past_participation
       
   106   Care just needs to be taken to keep the existing choice_ids and choices
       
   107   compatible.
       
   108   
       
   109   Other interesting possibilities also exist, such as asking about GSoC
       
   110   participation of the GHOP participants (some GHOP high school students
       
   111   have actually previously been GSoC mentors, for example).  To produce
       
   112   unique statistics for GHOP that could also be aggregated overall in
       
   113   combination with GSoC, the gsoc_past_participation Question would be
       
   114   duplicated (unaltered) to something like:
       
   115     Question:google/ghop2009/gsoc_past_participation
       
   116   To get the combined results, query on a link_name of
       
   117   gsoc_past_participation.  For more targeted results, include the
       
   118   partial_path to make the query more specific.
       
   119 
       
   120   Question creation to permit use cases like the one above is going to
       
   121   be a bit of an "advanced" skill, possibly.  "Doing it wrong" the first
       
   122   time a Question is created will make it difficult to implement stuff
       
   123   like multiple-choice Questions that "grow" new choices year-over-year.
       
   124 
       
   125   A dynamic form is most definitely going to be needed to implement the
       
   126   Question creation and editing for multiple-choice questions.
       
   127   """
       
   128   #: db.ListProperty of short, plain-text, "link_name-like" strings
       
   129   #: representing the "encoded" answer choices (must be strings compatible
       
   130   #: with being query arguments and being used in HTML controls and POST
       
   131   #: responses).
       
   132   #:
       
   133   #: If empty (None or an empty list), it is assumed that this Question
       
   134   #: is *not* a multiple choice question.  In that case, the UI should
       
   135   #: display the Question as a textarea in forms and accept any plain-text.
       
   136   #:
       
   137   #: If non-empty, max_answers helps determine how the UI should display
       
   138   #: the Question.  Also, controller logic needs to validate if the
       
   139   #: strings in the 'answers' property of the Answer entity come only
       
   140   #: from this list.
       
   141   #:
       
   142   #: Once Answers to this Question have been stored in the Datastore,
       
   143   #: choice_ids and choices should *not* be modified.  An existing
       
   144   #: Question can be duplicated and then modified (but, it will be a
       
   145   #: different question as a result).
       
   146   choice_ids = db.ListProperty(item_type=str)
       
   147 
       
   148   #: db.ListProperty of human-readable choice strings, in the same order
       
   149   #: as, and corresponding to, the "encoded" choices in the choice_ids
       
   150   #: db.ListProperty. 
       
   151   choices = db.ListProperty(item_type=str)
       
   152 
       
   153   #: db.IntegerProperty indicating the maximum number of answer values
       
   154   #: permitted for this question.  If 'choices' does not contain a list of
       
   155   #: choice strings, this value is ignored (but should still only be 1).
       
   156   #:
       
   157   #: If there are 'choices' and this value is 1, the UI should render the
       
   158   #: Question in forms as a single-choice control ("radio buttons").
       
   159   #:
       
   160   #: If there are 'choices' and this value is greater than 1, the UI should
       
   161   #: render the question as a list of check-boxes.
       
   162   #:
       
   163   #: max_answers greater than 1 combined with choices enables Questions
       
   164   #: like, for example, "...select the three most important...".
       
   165   max_answers = db.IntegerProperty(default=1)
       
   166 
       
   167   #: field storing whether the Answer to a Question is optional
       
   168   is_optional = db.BooleanProperty(default=False)