{"action":"created","comment":{"url":"https://api.github.com/repos/open-craft/xblock-mentoring/pulls/comments/27231963","id":27231963,"diff_hunk":"@@ -24,39 +21,146 @@\n # Imports ###########################################################\n \n import logging\n-\n from lazy import lazy\n \n-from xblock.fragment import Fragment\n-\n-from .light_children import LightChild, Boolean, Scope, String\n from .models import Answer\n-from .utils import render_template\n+\n+from xblock.core import XBlock\n+from xblock.fields import Scope, Float, Integer, String\n+from xblock.fragment import Fragment\n+from xblock.validation import ValidationMessage\n+from xblockutils.resources import ResourceLoader\n+from xblockutils.studio_editable import StudioEditableXBlockMixin\n+from .step import StepMixin\n+import uuid\n \n \n # Globals ###########################################################\n \n log = logging.getLogger(__name__)\n+loader = ResourceLoader(__name__)\n+\n \n+# Make '_' a no-op so we can scrape strings\n+def _(text):\n+    return text\n \n # Classes ###########################################################\n \n-class AnswerBlock(LightChild):\n+\n+class AnswerMixin(object):\n+    \"\"\"\n+    Mixin to give an XBlock the ability to read/write data to the Answers DB table.\n+    \"\"\"\n+    def _get_course_id(self):\n+        \"\"\" Get a course ID if available \"\"\"\n+        return getattr(self.runtime, 'course_id', 'all')\n+\n+    def _get_student_id(self):\n+        \"\"\" Get student anonymous ID or normal ID \"\"\"\n+        try:\n+            return self.runtime.anonymous_student_id\n+        except AttributeError:\n+            return self.scope_ids.user_id\n+\n+    def get_model_object(self, name=None):\n+        \"\"\"\n+        Fetches the Answer model object for the answer named `name`\n+        \"\"\"\n+        # By default, get the model object for the current answer's name\n+        if not name:\n+            name = self.name\n+        # Consistency check - we should have a name by now\n+        if not name:\n+            raise ValueError('AnswerBlock.name field need to be set to a non-null/empty value')\n+\n+        student_id = self._get_student_id()\n+        course_id = self._get_course_id()\n+\n+        answer_data, _ = Answer.objects.get_or_create(\n+            student_id=student_id,\n+            course_id=course_id,\n+            name=name,\n+        )\n+        return answer_data\n+\n+    def validate_field_data(self, validation, data):\n+        \"\"\"\n+        Validate this block's field data.\n+        \"\"\"\n+        super(AnswerMixin, self).validate_field_data(validation, data)\n+\n+        def add_error(msg):\n+            validation.add(ValidationMessage(ValidationMessage.ERROR, msg))\n+\n+        if not data.name:\n+            add_error(u\"A Question ID is required.\")\n+\n+    def _(self, text):\n+        \"\"\" translate text \"\"\"\n+        return self.runtime.service(self, \"i18n\").ugettext(text)\n+\n+\n+@XBlock.needs(\"i18n\")\n+class AnswerBlock(AnswerMixin, StepMixin, StudioEditableXBlockMixin, XBlock):\n     \"\"\"\n     A field where the student enters an answer\n \n     Must be included as a child of a mentoring block. Answers are persisted as django model instances\n     to make them searchable and referenceable across xblocks.\n     \"\"\"\n-    read_only = Boolean(help=\"Display as a read-only field\", default=False, scope=Scope.content)\n-    default_from = String(help=\"If specified, the name of the answer to get the default value from\",\n-                          default=None, scope=Scope.content)\n+    name = String(\n+        display_name=_(\"Question ID (name)\"),\n+        help=_(\"The ID of this block. Should be unique unless you want the answer to be used in multiple places.\"),\n+        default=\"\",\n+        scope=Scope.content\n+    )\n+    default_from = String(\n+        display_name=_(\"Default From\"),\n+        help=_(\"If a question ID is specified, get the default value from this answer.\"),\n+        default=None,\n+        scope=Scope.content\n+    )\n+    min_characters = Integer(\n+        display_name=_(\"Min. Allowed Characters\"),\n+        help=_(\"Minimum number of characters allowed for the answer\"),\n+        default=0,\n+        scope=Scope.content\n+    )\n+    question = String(\n+        display_name=_(\"Question\"),\n+        help=_(\"Question to ask the student\"),\n+        scope=Scope.content,\n+        default=\"\"\n+    )\n+    weight = Float(\n+        display_name=_(\"Weight\"),\n+        help=_(\"Defines the maximum total grade of the answer block.\"),\n+        default=1,\n+        scope=Scope.settings,\n+        enforce_type=True\n+    )\n+\n+    editable_fields = ('question', 'name', 'min_characters', 'weight', 'default_from')\n+\n+    @property\n+    def studio_display_name(self):\n+        if not self.lonely_step:\n+            return self._(u\"Question {number}\").format(number=self.step_number)\n+        return self._(u\"Question\")\n+\n+    def __getattribute__(self, name):","path":"mentoring/answer.py","position":152,"original_position":152,"commit_id":"670486507a484ccfd37e78ad14e76d3b71394eea","original_commit_id":"363a51924eebd7c25a796b6b884fd0baa71b25e1","user":{"login":"bradenmacdonald","id":945577,"avatar_url":"https://avatars.githubusercontent.com/u/945577?v=3","gravatar_id":"","url":"https://api.github.com/users/bradenmacdonald","html_url":"https://github.com/bradenmacdonald","followers_url":"https://api.github.com/users/bradenmacdonald/followers","following_url":"https://api.github.com/users/bradenmacdonald/following{/other_user}","gists_url":"https://api.github.com/users/bradenmacdonald/gists{/gist_id}","starred_url":"https://api.github.com/users/bradenmacdonald/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bradenmacdonald/subscriptions","organizations_url":"https://api.github.com/users/bradenmacdonald/orgs","repos_url":"https://api.github.com/users/bradenmacdonald/repos","events_url":"https://api.github.com/users/bradenmacdonald/events{/privacy}","received_events_url":"https://api.github.com/users/bradenmacdonald/received_events","type":"User","site_admin":false},"body":"@cpennington \r\n\r\nThe outcome I'm trying to get is that the title of this block as seen in Studio is a dynamically generated string (\"Question X\") based on this block's position among its parent's children. So it must be an instance method of some sort.\r\n\r\nAll I'm trying to avoid are errors in Studio and Studio displaying strings other than the string I want used as the title.\r\n\r\nIf I just do `@property def display_name(self):`, it causes errors in Studio, which tries to access the property as though it were a field, and then gives `AttributeError: display_name.default does not exist`. I think a mixin injected by the edX runtime has already contributed a default `display_name` field, so Studio tries to treat this property as a field.\r\n\r\nThis seems to be what `display_name_with_default` is designed for, but if I implement that, Studio sometimes doesn't bother to read its value, and instead just display's the block type as the display_name in some cases. The following two screenshots show the same pb-mcq block on two different pages:\r\n\r\nCorrectly checks display_name_with_default:\r\n![screen shot 2015-03-26 at 9 48 59 am](https://cloud.githubusercontent.com/assets/945577/6851641/9a38fc7e-d39d-11e4-800d-bcdac2fd5f7f.png)\r\n\r\nWrong:\r\n![screen shot 2015-03-26 at 9 49 30 am](https://cloud.githubusercontent.com/assets/945577/6851658/a60a44a4-d39d-11e4-8748-90a2d115a9fd.png)\r\n\r\n\r\nAt this point, though, I think I should just use this approach and open a separate PR to make Studio's behavior consistent.","created_at":"2015-03-26T16:53:34Z","updated_at":"2015-03-26T16:53:34Z","html_url":"https://github.com/open-craft/xblock-mentoring/pull/4#discussion_r27231963","pull_request_url":"https://api.github.com/repos/open-craft/xblock-mentoring/pulls/4","_links":{"self":{"href":"https://api.github.com/repos/open-craft/xblock-mentoring/pulls/comments/27231963"},"html":{"href":"https://github.com/open-craft/xblock-mentoring/pull/4#discussion_r27231963"},"pull_request":{"href":"https://api.github.com/repos/open-craft/xblock-mentoring/pulls/4"}}},"pull_request":{"url":"https://api.github.com/repos/open-craft/xblock-mentoring/pulls/4","id":28813760,"html_url":"https://github.com/open-craft/xblock-mentoring/pull/4","diff_url":"https://github.com/open-craft/xblock-mentoring/pull/4.diff","patch_url":"https://github.com/open-craft/xblock-mentoring/pull/4.patch","issue_url":"https://api.github.com/repos/open-craft/xblock-mentoring/issues/4","number":4,"state":"open","locked":false,"title":"Unification of forks & conversion to real children","user":{"login":"antoviaque","id":514483,"avatar_url":"https://avatars.githubusercontent.com/u/514483?v=3","gravatar_id":"","url":"https://api.github.com/users/antoviaque","html_url":"https://github.com/antoviaque","followers_url":"https://api.github.com/users/antoviaque/followers","following_url":"https://api.github.com/users/antoviaque/following{/other_user}","gists_url":"https://api.github.com/users/antoviaque/gists{/gist_id}","starred_url":"https://api.github.com/users/antoviaque/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/antoviaque/subscriptions","organizations_url":"https://api.github.com/users/antoviaque/orgs","repos_url":"https://api.github.com/users/antoviaque/repos","events_url":"https://api.github.com/users/antoviaque/events{/privacy}","received_events_url":"https://api.github.com/users/antoviaque/received_events","type":"User","site_admin":false},"body":"Note that this PR isn't meant to be merged, it just serves to show the difference with what is on edx.org. Note that the two have slightly diverged, due to backports of features. We would likely switch the requirements used on edx.org to use the current repository's master rather than merging.\r\n\r\nSee https://openedx.atlassian.net/browse/OSPR-419\r\n\r\nSandbox: LMS: http://sandbox.opencraft.com/ - CMS: http://sandbox.opencraft.com:18010/","created_at":"2015-02-06T14:53:17Z","updated_at":"2015-03-26T16:53:34Z","closed_at":null,"merged_at":null,"merge_commit_sha":null,"assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/open-craft/xblock-mentoring/pulls/4/commits","review_comments_url":"https://api.github.com/repos/open-craft/xblock-mentoring/pulls/4/comments","review_comment_url":"https://api.github.com/repos/open-craft/xblock-mentoring/pulls/comments{/number}","comments_url":"https://api.github.com/repos/open-craft/xblock-mentoring/issues/4/comments","statuses_url":"https://api.github.com/repos/open-craft/xblock-mentoring/statuses/670486507a484ccfd37e78ad14e76d3b71394eea","head":{"label":"open-craft:master","ref":"master","sha":"670486507a484ccfd37e78ad14e76d3b71394eea","user":{"login":"open-craft","id":7414786,"avatar_url":"https://avatars.githubusercontent.com/u/7414786?v=3","gravatar_id":"","url":"https://api.github.com/users/open-craft","html_url":"https://github.com/open-craft","followers_url":"https://api.github.com/users/open-craft/followers","following_url":"https://api.github.com/users/open-craft/following{/other_user}","gists_url":"https://api.github.com/users/open-craft/gists{/gist_id}","starred_url":"https://api.github.com/users/open-craft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/open-craft/subscriptions","organizations_url":"https://api.github.com/users/open-craft/orgs","repos_url":"https://api.github.com/users/open-craft/repos","events_url":"https://api.github.com/users/open-craft/events{/privacy}","received_events_url":"https://api.github.com/users/open-craft/received_events","type":"Organization","site_admin":false},"repo":{"id":29684458,"name":"xblock-mentoring","full_name":"open-craft/xblock-mentoring","owner":{"login":"open-craft","id":7414786,"avatar_url":"https://avatars.githubusercontent.com/u/7414786?v=3","gravatar_id":"","url":"https://api.github.com/users/open-craft","html_url":"https://github.com/open-craft","followers_url":"https://api.github.com/users/open-craft/followers","following_url":"https://api.github.com/users/open-craft/following{/other_user}","gists_url":"https://api.github.com/users/open-craft/gists{/gist_id}","starred_url":"https://api.github.com/users/open-craft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/open-craft/subscriptions","organizations_url":"https://api.github.com/users/open-craft/orgs","repos_url":"https://api.github.com/users/open-craft/repos","events_url":"https://api.github.com/users/open-craft/events{/privacy}","received_events_url":"https://api.github.com/users/open-craft/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/open-craft/xblock-mentoring","description":"Mentoring XBlock for edX","fork":true,"url":"https://api.github.com/repos/open-craft/xblock-mentoring","forks_url":"https://api.github.com/repos/open-craft/xblock-mentoring/forks","keys_url":"https://api.github.com/repos/open-craft/xblock-mentoring/keys{/key_id}","collaborators_url":"https://api.github.com/repos/open-craft/xblock-mentoring/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/open-craft/xblock-mentoring/teams","hooks_url":"https://api.github.com/repos/open-craft/xblock-mentoring/hooks","issue_events_url":"https://api.github.com/repos/open-craft/xblock-mentoring/issues/events{/number}","events_url":"https://api.github.com/repos/open-craft/xblock-mentoring/events","assignees_url":"https://api.github.com/repos/open-craft/xblock-mentoring/assignees{/user}","branches_url":"https://api.github.com/repos/open-craft/xblock-mentoring/branches{/branch}","tags_url":"https://api.github.com/repos/open-craft/xblock-mentoring/tags","blobs_url":"https://api.github.com/repos/open-craft/xblock-mentoring/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/open-craft/xblock-mentoring/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/open-craft/xblock-mentoring/git/refs{/sha}","trees_url":"https://api.github.com/repos/open-craft/xblock-mentoring/git/trees{/sha}","statuses_url":"https://api.github.com/repos/open-craft/xblock-mentoring/statuses/{sha}","languages_url":"https://api.github.com/repos/open-craft/xblock-mentoring/languages","stargazers_url":"https://api.github.com/repos/open-craft/xblock-mentoring/stargazers","contributors_url":"https://api.github.com/repos/open-craft/xblock-mentoring/contributors","subscribers_url":"https://api.github.com/repos/open-craft/xblock-mentoring/subscribers","subscription_url":"https://api.github.com/repos/open-craft/xblock-mentoring/subscription","commits_url":"https://api.github.com/repos/open-craft/xblock-mentoring/commits{/sha}","git_commits_url":"https://api.github.com/repos/open-craft/xblock-mentoring/git/commits{/sha}","comments_url":"https://api.github.com/repos/open-craft/xblock-mentoring/comments{/number}","issue_comment_url":"https://api.github.com/repos/open-craft/xblock-mentoring/issues/comments{/number}","contents_url":"https://api.github.com/repos/open-craft/xblock-mentoring/contents/{+path}","compare_url":"https://api.github.com/repos/open-craft/xblock-mentoring/compare/{base}...{head}","merges_url":"https://api.github.com/repos/open-craft/xblock-mentoring/merges","archive_url":"https://api.github.com/repos/open-craft/xblock-mentoring/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/open-craft/xblock-mentoring/downloads","issues_url":"https://api.github.com/repos/open-craft/xblock-mentoring/issues{/number}","pulls_url":"https://api.github.com/repos/open-craft/xblock-mentoring/pulls{/number}","milestones_url":"https://api.github.com/repos/open-craft/xblock-mentoring/milestones{/number}","notifications_url":"https://api.github.com/repos/open-craft/xblock-mentoring/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/open-craft/xblock-mentoring/labels{/name}","releases_url":"https://api.github.com/repos/open-craft/xblock-mentoring/releases{/id}","created_at":"2015-01-22T15:11:16Z","updated_at":"2015-03-24T20:32:32Z","pushed_at":"2015-03-26T05:33:15Z","git_url":"git://github.com/open-craft/xblock-mentoring.git","ssh_url":"git@github.com:open-craft/xblock-mentoring.git","clone_url":"https://github.com/open-craft/xblock-mentoring.git","svn_url":"https://github.com/open-craft/xblock-mentoring","homepage":null,"size":2702,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":3,"forks":0,"open_issues":3,"watchers":0,"default_branch":"master"}},"base":{"label":"open-craft:gsehub","ref":"gsehub","sha":"fd472edde6ea1455333a955d7b1942378ae68bd8","user":{"login":"open-craft","id":7414786,"avatar_url":"https://avatars.githubusercontent.com/u/7414786?v=3","gravatar_id":"","url":"https://api.github.com/users/open-craft","html_url":"https://github.com/open-craft","followers_url":"https://api.github.com/users/open-craft/followers","following_url":"https://api.github.com/users/open-craft/following{/other_user}","gists_url":"https://api.github.com/users/open-craft/gists{/gist_id}","starred_url":"https://api.github.com/users/open-craft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/open-craft/subscriptions","organizations_url":"https://api.github.com/users/open-craft/orgs","repos_url":"https://api.github.com/users/open-craft/repos","events_url":"https://api.github.com/users/open-craft/events{/privacy}","received_events_url":"https://api.github.com/users/open-craft/received_events","type":"Organization","site_admin":false},"repo":{"id":29684458,"name":"xblock-mentoring","full_name":"open-craft/xblock-mentoring","owner":{"login":"open-craft","id":7414786,"avatar_url":"https://avatars.githubusercontent.com/u/7414786?v=3","gravatar_id":"","url":"https://api.github.com/users/open-craft","html_url":"https://github.com/open-craft","followers_url":"https://api.github.com/users/open-craft/followers","following_url":"https://api.github.com/users/open-craft/following{/other_user}","gists_url":"https://api.github.com/users/open-craft/gists{/gist_id}","starred_url":"https://api.github.com/users/open-craft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/open-craft/subscriptions","organizations_url":"https://api.github.com/users/open-craft/orgs","repos_url":"https://api.github.com/users/open-craft/repos","events_url":"https://api.github.com/users/open-craft/events{/privacy}","received_events_url":"https://api.github.com/users/open-craft/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/open-craft/xblock-mentoring","description":"Mentoring XBlock for edX","fork":true,"url":"https://api.github.com/repos/open-craft/xblock-mentoring","forks_url":"https://api.github.com/repos/open-craft/xblock-mentoring/forks","keys_url":"https://api.github.com/repos/open-craft/xblock-mentoring/keys{/key_id}","collaborators_url":"https://api.github.com/repos/open-craft/xblock-mentoring/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/open-craft/xblock-mentoring/teams","hooks_url":"https://api.github.com/repos/open-craft/xblock-mentoring/hooks","issue_events_url":"https://api.github.com/repos/open-craft/xblock-mentoring/issues/events{/number}","events_url":"https://api.github.com/repos/open-craft/xblock-mentoring/events","assignees_url":"https://api.github.com/repos/open-craft/xblock-mentoring/assignees{/user}","branches_url":"https://api.github.com/repos/open-craft/xblock-mentoring/branches{/branch}","tags_url":"https://api.github.com/repos/open-craft/xblock-mentoring/tags","blobs_url":"https://api.github.com/repos/open-craft/xblock-mentoring/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/open-craft/xblock-mentoring/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/open-craft/xblock-mentoring/git/refs{/sha}","trees_url":"https://api.github.com/repos/open-craft/xblock-mentoring/git/trees{/sha}","statuses_url":"https://api.github.com/repos/open-craft/xblock-mentoring/statuses/{sha}","languages_url":"https://api.github.com/repos/open-craft/xblock-mentoring/languages","stargazers_url":"https://api.github.com/repos/open-craft/xblock-mentoring/stargazers","contributors_url":"https://api.github.com/repos/open-craft/xblock-mentoring/contributors","subscribers_url":"https://api.github.com/repos/open-craft/xblock-mentoring/subscribers","subscription_url":"https://api.github.com/repos/open-craft/xblock-mentoring/subscription","commits_url":"https://api.github.com/repos/open-craft/xblock-mentoring/commits{/sha}","git_commits_url":"https://api.github.com/repos/open-craft/xblock-mentoring/git/commits{/sha}","comments_url":"https://api.github.com/repos/open-craft/xblock-mentoring/comments{/number}","issue_comment_url":"https://api.github.com/repos/open-craft/xblock-mentoring/issues/comments{/number}","contents_url":"https://api.github.com/repos/open-craft/xblock-mentoring/contents/{+path}","compare_url":"https://api.github.com/repos/open-craft/xblock-mentoring/compare/{base}...{head}","merges_url":"https://api.github.com/repos/open-craft/xblock-mentoring/merges","archive_url":"https://api.github.com/repos/open-craft/xblock-mentoring/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/open-craft/xblock-mentoring/downloads","issues_url":"https://api.github.com/repos/open-craft/xblock-mentoring/issues{/number}","pulls_url":"https://api.github.com/repos/open-craft/xblock-mentoring/pulls{/number}","milestones_url":"https://api.github.com/repos/open-craft/xblock-mentoring/milestones{/number}","notifications_url":"https://api.github.com/repos/open-craft/xblock-mentoring/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/open-craft/xblock-mentoring/labels{/name}","releases_url":"https://api.github.com/repos/open-craft/xblock-mentoring/releases{/id}","created_at":"2015-01-22T15:11:16Z","updated_at":"2015-03-24T20:32:32Z","pushed_at":"2015-03-26T05:33:15Z","git_url":"git://github.com/open-craft/xblock-mentoring.git","ssh_url":"git@github.com:open-craft/xblock-mentoring.git","clone_url":"https://github.com/open-craft/xblock-mentoring.git","svn_url":"https://github.com/open-craft/xblock-mentoring","homepage":null,"size":2702,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":3,"forks":0,"open_issues":3,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/open-craft/xblock-mentoring/pulls/4"},"html":{"href":"https://github.com/open-craft/xblock-mentoring/pull/4"},"issue":{"href":"https://api.github.com/repos/open-craft/xblock-mentoring/issues/4"},"comments":{"href":"https://api.github.com/repos/open-craft/xblock-mentoring/issues/4/comments"},"review_comments":{"href":"https://api.github.com/repos/open-craft/xblock-mentoring/pulls/4/comments"},"review_comment":{"href":"https://api.github.com/repos/open-craft/xblock-mentoring/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/open-craft/xblock-mentoring/pulls/4/commits"},"statuses":{"href":"https://api.github.com/repos/open-craft/xblock-mentoring/statuses/670486507a484ccfd37e78ad14e76d3b71394eea"}}}}