Move LINK_ID and SCOPE_PATH regular expressions to soc/models/linkable.py.
Fix some too-long lines. Change key names back to type:scope_path/link_id
(instead of type:scope_path:link_id). Some minor __doc__ string updates.
Patch by: Todd Larsen
--- a/app/soc/logic/models/base.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/logic/models/base.py Fri Nov 21 08:41:23 2008 +0000
@@ -56,7 +56,7 @@
if name:
self._name = name
else:
- self._name = self.getModelClassName()
+ self._name = self._model.__name__
if skip_properties:
self._skip_properties = skip_properties
@@ -101,18 +101,12 @@
keyvalues.append(kwargs[key_field_name])
# construct the KeyName in the appropriate format
- return ":".join([self._name] + keyvalues)
-
- def getModelClassName(self):
- """Returns model class name string.
- """
- return self._model.__name__
+ return "%s:%s" % (self._name, '/'.join(keyvalues))
def getFullModelClassName(self):
"""Returns fully-qualified model module.class name string.
"""
- return '%s.%s' % (self._model.__module__,
- self.getModelClassName())
+ return '%s.%s' % (self._model.__module__, self._model.__name__)
def getKeyValues(self, entity):
"""Exctracts the key values from entity and returns them.
@@ -474,6 +468,10 @@
supplied key_name and properties.
"""
+ import logging
+ logging.info(key_name)
+ logging.info(properties)
+
entity = self.getFromKeyName(key_name)
if not entity:
--- a/app/soc/logic/models/user.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/logic/models/user.py Fri Nov 21 08:41:23 2008 +0000
@@ -40,7 +40,8 @@
def isFormerAccount(self, account):
"""Returns true if account is a former account of some User.
"""
- # TODO(pawel.solyga): replace 1000 with solution that works for any number of queries
+ # TODO(pawel.solyga): replace 1000 with solution that works for any
+ # number of queries
users_with_former_accounts = soc.models.user.User.gql(
'WHERE former_accounts != :1', None).fetch(1000)
@@ -74,28 +75,13 @@
return ['link_id']
- def updateOrCreateFromAccount(self, properties, account):
- """Like updateOrCreateFromKeyName, but resolves account to key_name first.
- """
-
- # attempt to retrieve the existing entity
- user = soc.models.user.User.gql('WHERE account = :1', account).get()
-
- if user:
- key_name = user.key().name()
- else:
- raise
- key_name = self.getKeyNameForFields({'link_id': properties['link_id']})
-
- return self.updateOrCreateFromKeyName(properties, key_name)
-
def _updateField(self, model, name, value):
"""Special case logic for account.
When the account is changed, the former_accounts field should be appended
with the old account.
"""
- if name == 'account' and model.account != value:
+ if (name == 'account') and (model.account != value):
model.former_accounts.append(model.account)
return True
--- a/app/soc/logic/path_link_name.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/logic/path_link_name.py Fri Nov 21 08:41:23 2008 +0000
@@ -25,37 +25,7 @@
import re
-
-# start with ASCII digit or lowercase
-# (additional ASCII digit or lowercase
-# -OR-
-# underscore and ASCII digit or lowercase)
-# zero or more of OR group
-LINK_ID_PATTERN_CORE = r'[0-9a-z](?:[0-9a-z]|_[0-9a-z])*'
-LINK_ID_ARG_PATTERN = r'(?P<link_id>%s)' % LINK_ID_PATTERN_CORE
-LINK_ID_PATTERN = r'^%s$' % LINK_ID_PATTERN_CORE
-LINK_ID_REGEX = re.compile(LINK_ID_PATTERN)
-
-# scope path is multiple link_id chunks,
-# each separated by a trailing /
-# (at least 1)
-SCOPE_PATH_ARG_PATTERN = (r'(?P<scope_path>%(link_id)s'
- '(?:/%(link_id)s)*)' % {
- 'link_id': LINK_ID_PATTERN_CORE})
-SCOPE_PATH_PATTERN = r'^%s$' % SCOPE_PATH_ARG_PATTERN
-SCOPE_PATH_REGEX = re.compile(SCOPE_PATH_PATTERN)
-
-# path is multiple link_id chunks,
-# each separated by a trailing /
-# (at least 1)
-# followed by a single link_id with no trailing /
-PATH_LINK_ID_ARGS_PATTERN = (
- r'%(scope_path)s/'
- '(?P<link_id>%(link_id)s)' % {
- 'scope_path' : SCOPE_PATH_ARG_PATTERN,
- 'link_id': LINK_ID_PATTERN_CORE})
-PATH_LINK_ID_PATTERN = r'^%s$' % PATH_LINK_ID_ARGS_PATTERN
-PATH_LINK_ID_REGEX = re.compile(PATH_LINK_ID_PATTERN)
+from soc.models import linkable
def getPartsFromPath(path):
@@ -66,7 +36,7 @@
'link_id': 'link_id'}
or {} (empty dict) if string did not match PATH_LINK_ID_PATTERN.
"""
- path_link_name_match = PATH_LINK_ID_REGEX.match(path)
+ path_link_name_match = linkable.PATH_LINK_ID_REGEX.match(path)
if not path_link_name_match:
return {}
--- a/app/soc/logic/validate.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/logic/validate.py Fri Nov 21 08:41:23 2008 +0000
@@ -27,7 +27,7 @@
from google.appengine.api import urlfetch
-from soc.logic import path_link_name
+from soc.models import linkable
def isFeedURLValid(feed_url=None):
@@ -51,7 +51,7 @@
Args:
link_id: link ID used in URLs for identification
"""
- if path_link_name.LINK_ID_REGEX.match(link_id):
+ if linkable.LINK_ID_REGEX.match(link_id):
return True
return False
@@ -64,7 +64,7 @@
used for identification.
"""
- if path_link_name.SCOPE_PATH_REGEX.match(scope_path):
+ if linkable.SCOPE_PATH_REGEX.match(scope_path):
return True
return False
--- a/app/soc/models/group.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/models/group.py Fri Nov 21 08:41:23 2008 +0000
@@ -22,18 +22,17 @@
]
-import polymodel
-
from google.appengine.ext import db
from django.utils.translation import ugettext_lazy
from soc.models import countries
+import soc.models.linkable
import soc.models.user
-class Group(polymodel.PolyModel):
+class Group(soc.models.linkable.Linkable):
"""Common data fields for all groups.
"""
@@ -42,14 +41,6 @@
verbose_name=ugettext_lazy('Name'))
name.help_text = ugettext_lazy('Complete, formal name of the group.')
- #: Required field storing link_id used in URLs to identify group.
- #: Lower ASCII characters only.
- link_id = db.StringProperty(required=True,
- verbose_name=ugettext_lazy('Link ID'))
- link_id.help_text = ugettext_lazy(
- 'Field used in URLs to identify group. '
- 'Lower ASCII characters only.')
-
#: Required field storing short name of the group.
#: It can be used for displaying group as sidebar menu item.
short_name = db.StringProperty(required=True,
--- a/app/soc/models/home_settings.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/models/home_settings.py Fri Nov 21 08:41:23 2008 +0000
@@ -22,16 +22,15 @@
]
-import polymodel
-
from google.appengine.ext import db
from django.utils.translation import ugettext_lazy
import soc.models.document
+import soc.models.linkable
-class HomeSettings(polymodel.PolyModel):
+class HomeSettings(soc.models.linkable.Linkable):
"""Model that stores settings for various Home pages.
This Model is the basis for more specific "/home" view settings, such as
@@ -51,14 +50,3 @@
feed_url.help_text = ugettext_lazy(
'The URL should be a valid ATOM or RSS feed. '
'Feed entries are shown on the home page.')
-
- #: Required path, prepended to a "link ID" to form the Setting URL.
- scope_path = db.StringProperty(required=True,
- verbose_name=ugettext_lazy('Settings scope path'))
- scope_path.help_text = ugettext_lazy(
- 'path portion of URLs for Settings, prepended to link ID')
-
- #: Required link ID, appended to a "path" to form the Setting URL.
- link_id = db.StringProperty(required=True,
- verbose_name=ugettext_lazy('Setttings link ID'))
- link_id.help_text = ugettext_lazy('link ID for Settings used in URLs')
--- a/app/soc/models/linkable.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/models/linkable.py Fri Nov 21 08:41:23 2008 +0000
@@ -44,6 +44,27 @@
LINK_ID_PATTERN = r'^%s$' % LINK_ID_PATTERN_CORE
LINK_ID_REGEX = re.compile(LINK_ID_PATTERN)
+# scope path is multiple link_id chunks,
+# each separated by a trailing /
+# (at least 1)
+SCOPE_PATH_ARG_PATTERN = (r'(?P<scope_path>%(link_id)s'
+ '(?:/%(link_id)s)*)' % {
+ 'link_id': LINK_ID_PATTERN_CORE})
+SCOPE_PATH_PATTERN = r'^%s$' % SCOPE_PATH_ARG_PATTERN
+SCOPE_PATH_REGEX = re.compile(SCOPE_PATH_PATTERN)
+
+# path is multiple link_id chunks,
+# each separated by a trailing /
+# (at least 1)
+# followed by a single link_id with no trailing /
+PATH_LINK_ID_ARGS_PATTERN = (
+ r'%(scope_path)s/'
+ '(?P<link_id>%(link_id)s)' % {
+ 'scope_path' : SCOPE_PATH_ARG_PATTERN,
+ 'link_id': LINK_ID_PATTERN_CORE})
+PATH_LINK_ID_PATTERN = r'^%s$' % PATH_LINK_ID_ARGS_PATTERN
+PATH_LINK_ID_REGEX = re.compile(PATH_LINK_ID_PATTERN)
+
class Linkable(polymodel.PolyModel):
"""A base class for Model classes that are "linkable".
--- a/app/soc/models/question.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/models/question.py Fri Nov 21 08:41:23 2008 +0000
@@ -44,15 +44,15 @@
work.reviews: even Questions can be "reviewed" (possibly commented
on during creation or annotated once put into use).
- work.scope_path: used to scope (and, when combined with
- work.link_id, uniquely identify) a Question in the same way the
+ work.content: the Question text, asked to the respondent
+
+ linkable.scope: used to scope (and, when combined with
+ linkable.link_id, uniquely identify) a Question in the same way the
property are used with Documents, etc.
- work.link_id: used to identify (and, when combined with
- work.scope_path, *uniquely* identify) a Question in the same way
+ linkable.link_id: used to identify (and, when combined with
+ linkable.scope, *uniquely* identify) a Question in the same way
these properties are used with Documents, etc.
-
- work.content: the Question text, asked to the respondent
In addition to any explicit ReferenceProperties in the Question Model
and those inherited as described above, a Question entity participates
@@ -86,14 +86,14 @@
when these ideas are implemented in the views and controllers; they
are here now so that the concepts will not be lost before that time.
- The recommended use for the combination of work.scope_path and
- work.link_id is to keep the *same* link_id when copying and
+ The recommended use for the combination of linkable.scope and
+ linkable.link_id is to keep the *same* link_id when copying and
modifying an existing Question for a new Program (or instance of a
- Group that is per-Program), while changing the work.scope_path to
+ Group that is per-Program), while changing the linkable.scope to
represent the Program and Group "ownership" of the Question. For
example, if a Question asking about prior GSoC participation needed
to have an additional choice (see the choice_ids and choices properties
- below), it is desirable to keep the same work.link_id (and also
+ below), it is desirable to keep the same linkable.link_id (and also
simply append new choice_ids and choices to keep the old answer values
compatible). An existing Question in the above example might be identified
as something like:
@@ -116,7 +116,7 @@
Question:google/ghop2009/gsoc_past_participation
To get the combined results, query on a link_id of
gsoc_past_participation. For more targeted results, include the
- scope_path to make the query more specific.
+ scope to make the query more specific.
Question creation to permit use cases like the one above is going to
be a bit of an "advanced" skill, possibly. "Doing it wrong" the first
--- a/app/soc/models/quiz.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/models/quiz.py Fri Nov 21 08:41:23 2008 +0000
@@ -52,12 +52,13 @@
work.reviews: even Quizzes can be "reviewed" (possibly commented
on during creation or annotated once put into use).
- work.scope_path/work.link_id: used to scope and uniquely identify
- a Quiz in the same way these properties are used with Documents, etc.
-
work.content: the "preface" of the Quiz, displayed before any
of the Questions, usually containing instructions for the Quiz
+ linkable.scope/linkable.link_id: used to scope and uniquely
+ identify a Quiz in the same way these properties are used with
+ Documents, etc.
+
In addition to any explicit ReferenceProperties in the Quiz Model and
those inherited as described above, a Quiz entity participates in these
relationships:
--- a/app/soc/models/request.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/models/request.py Fri Nov 21 08:41:23 2008 +0000
@@ -21,8 +21,6 @@
]
-import polymodel
-
from google.appengine.ext import db
from django.utils.translation import ugettext_lazy
@@ -31,7 +29,7 @@
import soc.models.group
-class Request(polymodel.PolyModel):
+class Request(soc.models.linkable.Linkable):
"""A request is made to allow a person to create a new Role entity.
"""
--- a/app/soc/models/response.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/models/response.py Fri Nov 21 08:41:23 2008 +0000
@@ -21,15 +21,14 @@
]
-import polymodel
-
from google.appengine.ext import db
+import soc.models.linkable
import soc.models.quiz
import soc.models.user
-class Response(polymodel.PolyModel):
+class Response(soc.models.linkable.Linkable):
"""Model of a Response to a Quiz.
A Response is the "collection point" for a set of specific Answers to the
--- a/app/soc/models/review.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/models/review.py Fri Nov 21 08:41:23 2008 +0000
@@ -23,13 +23,14 @@
from google.appengine.ext import db
+import soc.models.linkable
# TODO: Uncomment when Survey model is committed
#import soc.models.survey
+import soc.models.reviewer
import soc.models.work
-import soc.models.reviewer
-class Review(db.Model):
+class Review(soc.models.linkable.Linkable):
"""Model of a review of a Proposal or a Task.
A Review entity is a specific instance of a completed Survey, collecting
--- a/app/soc/models/reviewer.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/models/reviewer.py Fri Nov 21 08:41:23 2008 +0000
@@ -23,8 +23,8 @@
from google.appengine.ext import db
+import soc.models.organization
import soc.models.role
-import soc.models.organization
class Reviewer(soc.models.role.Role):
--- a/app/soc/models/role.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/models/role.py Fri Nov 21 08:41:23 2008 +0000
@@ -23,18 +23,17 @@
]
-import polymodel
-
from google.appengine.ext import db
from django.utils.translation import ugettext_lazy
from soc.models import countries
+import soc.models.linkable
import soc.models.user
-class Role(polymodel.PolyModel):
+class Role(soc.models.linkable.Linkable):
"""Information common to Program participation for all Roles.
Some details of a Role are considered "public" information, and nearly
@@ -273,4 +272,4 @@
#: Optional field indicating choice of t-shirt fit; kept private.
tshirt_style = db.StringProperty(
verbose_name=ugettext_lazy('T-shirt Style'),
- choices=('male', 'female'))
\ No newline at end of file
+ choices=('male', 'female'))
--- a/app/soc/models/task.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/models/task.py Fri Nov 21 08:41:23 2008 +0000
@@ -25,10 +25,11 @@
from soc.models import base
+import soc.models.linkable
import soc.models.proposal
-class Task(base.ModelWithFieldAttributes):
+class Task(soc.models.linkable.Linkable):
"""Model of a Task, which is a Proposal to be completed by Contributors.
A Task brings along a Proposal that was used to initiate the Task. A Task
--- a/app/soc/models/user.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/models/user.py Fri Nov 21 08:41:23 2008 +0000
@@ -28,10 +28,10 @@
from django.utils.translation import ugettext_lazy
-from soc.models import base
+import soc.models.linkable
-class User(base.ModelWithFieldAttributes):
+class User(soc.models.linkable.Linkable):
"""A user and associated login credentials, the fundamental identity entity.
User is a separate Model class from Person because the same login
@@ -80,14 +80,6 @@
public_name = db.StringProperty(required=True,
verbose_name=ugettext_lazy('Public name'))
- #: Required field storing link_id used in URLs to identify user.
- #: Lower ASCII characters only.
- link_id = db.StringProperty(required=True,
- verbose_name=ugettext_lazy('Link ID'))
- link_id.help_text = ugettext_lazy(
- 'Field used in URLs to identify user. '
- 'Lower ASCII characters only.')
-
#: field storing whether User is a Developer with site-wide access.
is_developer = db.BooleanProperty(
verbose_name=ugettext_lazy('Is Developer'))
--- a/app/soc/models/work.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/models/work.py Fri Nov 21 08:41:23 2008 +0000
@@ -22,16 +22,15 @@
]
-import polymodel
-
from google.appengine.ext import db
from django.utils.translation import ugettext_lazy
+import soc.models.linkable
import soc.models.user
-class Work(polymodel.PolyModel):
+class Work(soc.models.linkable.Linkable):
"""Model of a Work created by one or more Persons in Roles.
Work is a "base entity" of other more specific "works" created by Persons
@@ -57,24 +56,6 @@
title.help_text = ugettext_lazy(
'title of the document; often used in the window title')
- #: Required path, prepended to a "link ID" to form the document URL.
- #: The combined path and link ID must be globally unique on the
- #: site. Except in /site/document (Developer) forms, this field is not
- #: usually directly editable by the User, but is instead set by controller
- #: logic to match the "scope" of the document.
- scope_path = db.StringProperty(required=True,
- verbose_name=ugettext_lazy('Scope path'))
- scope_path.help_text = ugettext_lazy(
- 'path portion of URLs, prepended to link ID')
-
- #: Required link ID, appended to a "path" to form the document URL.
- #: The combined path and link ID must be globally unique on the
- #: site (but, unlike some link IDs, a Work link ID can be reused,
- #: as long as the combination with the preceding path is unique).
- link_id = db.StringProperty(required=True,
- verbose_name=ugettext_lazy('Link ID'))
- link_id.help_text = ugettext_lazy('link ID used in URLs')
-
#: short name used in places such as the sidebar menu and breadcrumb trail
#: (optional: title will be used if short_name is not present)
short_name = db.StringProperty(verbose_name=ugettext_lazy('Short name'))
--- a/app/soc/views/models/base.py Fri Nov 21 08:38:53 2008 +0000
+++ b/app/soc/views/models/base.py Fri Nov 21 08:41:23 2008 +0000
@@ -36,7 +36,7 @@
from soc.logic import dicts
from soc.logic import models
-from soc.logic import path_link_name
+from soc.models import linkable
from soc.views import simple
from soc.views import helper
from soc.views.helper import access
@@ -532,7 +532,7 @@
patterns = params['key_fields_prefix']
for name in names:
- pattern = r'(?P<%s>%s)' % (name, path_link_name.LINK_ID_PATTERN_CORE)
+ pattern = r'(?P<%s>%s)' % (name, linkable.LINK_ID_PATTERN_CORE)
patterns.append(pattern)
result = '/'.join(patterns)
@@ -620,8 +620,8 @@
url = url % {
'url_name': params['url_name'],
- 'lnp': path_link_name.LINK_ID_ARG_PATTERN,
- 'ulnp': path_link_name.LINK_ID_PATTERN_CORE,
+ 'lnp': linkable.LINK_ID_ARG_PATTERN,
+ 'ulnp': linkable.LINK_ID_PATTERN_CORE,
'key_fields': key_fields_pattern,
}