Move path and link_name related functions and regex patterns to a new
authorTodd Larsen <tlarsen@google.com>
Wed, 01 Oct 2008 20:52:39 +0000
changeset 251 8f23804302d0
parent 250 4d7bf6bdcd8f
child 252 112f36f4598f
Move path and link_name related functions and regex patterns to a new soc/logic/path_linkname.py module, and fix all dependencies. path/link_name functionality is becoming the generic way to identify entities in the Datastore in a URL-compatible way.
app/soc/logic/document.py
app/soc/logic/key_name.py
app/soc/logic/path_linkname.py
app/soc/logic/validate.py
app/soc/views/site/docs/edit.py
--- a/app/soc/logic/document.py	Wed Oct 01 20:48:58 2008 +0000
+++ b/app/soc/logic/document.py	Wed Oct 01 20:52:39 2008 +0000
@@ -19,6 +19,7 @@
 
 __authors__ = [
   '"Pawel Solyga" <pawel.solyga@gmail.com>',
+  '"Todd Larsen" <tlarsen@google.com>',
   ]
 
 
@@ -36,24 +37,6 @@
 import soc.models.work
 
 
-WORK_PATH_LINKNAME_REGEX = re.compile(key_name.WORK_PATH_LINKNAME_PATTERN)
-
-def getPartsFromPath(path):
-  """Splits path string into partial_path and link_name.
-  
-  Returns:
-    {'partial_path': 'everything/but',
-     'link_name': 'link_name'}
-    or {} (empty dict) if string did not match WORK_PATH_LINKNAME_PATTERN.
-  """
-  path_linkname_match = WORK_PATH_LINKNAME_REGEX.match(path)
-  
-  if not path_linkname_match:
-    return {}
-
-  return path_linkname_match.groupdict()
-
-
 def getDocument(path, link_name=None):
   """Returns Document entity for a given path, or None if not found.  
     
--- a/app/soc/logic/key_name.py	Wed Oct 01 20:48:58 2008 +0000
+++ b/app/soc/logic/key_name.py	Wed Oct 01 20:52:39 2008 +0000
@@ -23,27 +23,7 @@
   ]
 
 
-# start with ASCII digit or lowercase
-#   (additional ASCII digit or lowercase
-#     -OR-
-#   underscore and ASCII digit or lowercase)
-#     zero or more of OR group
-LINKNAME_PATTERN_CORE = r'[0-9a-z](?:[0-9a-z]|_[0-9a-z])*'
-
-LINKNAME_PATTERN = r'^%s$' % LINKNAME_PATTERN_CORE
-LINKNAME_ARG_PATTERN = r'(?P<linkname>%s)' % LINKNAME_PATTERN_CORE
-
-
-# partial path is multiple linkname chunks,
-#   each separated by a trailing /
-#     (at least 1)
-# followed by a single linkname with no trailing /
-WORK_PATH_LINKNAME_ARGS_PATTERN = (
-    r'(?P<partial_path>%(linkname)s(?:/%(linkname)s)*)/'
-     '(?P<linkname>%(linkname)s)' % {
-        'linkname': LINKNAME_PATTERN_CORE})
-
-WORK_PATH_LINKNAME_PATTERN = r'^%s$' % WORK_PATH_LINKNAME_ARGS_PATTERN
+from soc.logic import path_linkname
 
 
 class Error(Exception):
@@ -56,45 +36,6 @@
   return '%s.%s' % (cls.__module__, cls.__name__) 
 
 
-def combinePath(path_parts):
-  """Returns path components combined into a single string.
-  
-  Args:
-    path_parts: a single path string, or a list of path part strings,
-      or a nested list of path part strings (where the zeroeth element in
-      the list is itself a list); for example:
-        'a/complete/path/in/one/string'
-        ['some', 'path', 'parts']
-        [['path', 'parts', 'and', 'a'], 'link name']
-
-  Returns:
-    None if path_parts is False (None, empty string, etc.) or if
-    any list elements are False (an empty list, empty string, etc.);
-    otherwise, the combined string with the necessary separators.
-  """
-  if not path_parts:
-    # completely empty input, so return early
-    return None
-
-  if not isinstance(path_parts, (list, tuple)):
-    # a single path string, so just return it as-is (nothing to do)
-    return path_parts
-  
-  flattened_parts = []
-  
-  for part in path_parts:
-    if not part:
-      # encountered a "False" element, which invalidates everything else
-      return None    
-  
-    if isinstance(part, (list, tuple)):
-      flattened_parts.extend(part)
-    else:
-      flattened_parts.append(part)
-
-  return '/'.join(flattened_parts)
-
-
 def nameDocument(partial_path, link_name=None):
   """Returns a Document key name constructed from a path and link name.
     
@@ -114,7 +55,7 @@
   if link_name:
     path.append(link_name)
 
-  path = combinePath(path)
+  path = path_linkname.combinePath(path)
 
   if not path:
     raise Error('"path" must be non-False: "%s"' % path)
@@ -170,7 +111,7 @@
     Error if sponsor_ln, program_ln, and link_Name combine to produce
     a "False" path (None, empty string, etc.)
   """
-  path = combinePath([[sponsor_ln, program_ln], link_name])
+  path = path_linkname.combinePath([[sponsor_ln, program_ln], link_name])
   
   if not path:
     raise Error('"path" must be non-False: "%s"' % path)
@@ -190,7 +131,7 @@
     Error if sponsor_ln, program_ln, and link_Name combine to produce
     a "False" path (None, empty string, etc.)
   """
-  path = combinePath([[sponsor_ln, program_ln], link_name])
+  path = path_linkname.combinePath([[sponsor_ln, program_ln], link_name])
   
   if not path:
     raise Error('"path" must be non-False: "%s"' % path)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/soc/logic/path_linkname.py	Wed Oct 01 20:52:39 2008 +0000
@@ -0,0 +1,103 @@
+#!/usr/bin/python2.5
+#
+# Copyright 2008 the Melange authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Path and link name manipulation functions.
+"""
+
+__authors__ = [
+  '"Todd Larsen" <tlarsen@google.com>',
+  ]
+
+
+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
+LINKNAME_PATTERN_CORE = r'[0-9a-z](?:[0-9a-z]|_[0-9a-z])*'
+LINKNAME_ARG_PATTERN = r'(?P<linkname>%s)' % LINKNAME_PATTERN_CORE
+LINKNAME_PATTERN = r'^%s$' % LINKNAME_PATTERN_CORE
+LINKNAME_REGEX = re.compile(LINKNAME_PATTERN)
+
+# partial path is multiple linkname chunks,
+#   each separated by a trailing /
+#     (at least 1)
+# followed by a single linkname with no trailing /
+PATH_LINKNAME_ARGS_PATTERN = (
+    r'(?P<partial_path>%(linkname)s(?:/%(linkname)s)*)/'
+     '(?P<linkname>%(linkname)s)' % {
+        'linkname': LINKNAME_PATTERN_CORE})
+
+PATH_LINKNAME_PATTERN = r'^%s$' % PATH_LINKNAME_ARGS_PATTERN
+PATH_LINKNAME_REGEX = re.compile(PATH_LINKNAME_PATTERN)
+
+
+def getPartsFromPath(path):
+  """Splits path string into partial_path and link_name.
+  
+  Returns:
+    {'partial_path': 'everything/but',
+     'link_name': 'link_name'}
+    or {} (empty dict) if string did not match PATH_LINKNAME_PATTERN.
+  """
+  path_linkname_match = PATH_LINKNAME_REGEX.match(path)
+  
+  if not path_linkname_match:
+    return {}
+
+  return path_linkname_match.groupdict()
+
+
+def combinePath(path_parts):
+  """Returns path components combined into a single string.
+  
+  Args:
+    path_parts: a single path string, or a list of path part strings,
+      or a nested list of path part strings (where the zeroeth element in
+      the list is itself a list); for example:
+        'a/complete/path/in/one/string'
+        ['some', 'path', 'parts']
+        [['path', 'parts', 'and', 'a'], 'link name']
+
+  Returns:
+    None if path_parts is False (None, empty string, etc.) or if
+    any list elements are False (an empty list, empty string, etc.);
+    otherwise, the combined string with the necessary separators.
+  """
+  if not path_parts:
+    # completely empty input, so return early
+    return None
+
+  if not isinstance(path_parts, (list, tuple)):
+    # a single path string, so just return it as-is (nothing to do)
+    return path_parts
+  
+  flattened_parts = []
+  
+  for part in path_parts:
+    if not part:
+      # encountered a "False" element, which invalidates everything else
+      return None    
+  
+    if isinstance(part, (list, tuple)):
+      flattened_parts.extend(part)
+    else:
+      flattened_parts.append(part)
+
+  return '/'.join(flattened_parts)
--- a/app/soc/logic/validate.py	Wed Oct 01 20:48:58 2008 +0000
+++ b/app/soc/logic/validate.py	Wed Oct 01 20:52:39 2008 +0000
@@ -28,6 +28,8 @@
 
 import feedparser
 
+from soc.logic import path_linkname
+
 
 def isFeedURLValid(feed_url=None):
   """Returns True if provided url is valid ATOM or RSS.
@@ -44,25 +46,12 @@
   return False
 
 
-LINKNAME_PATTERN = r'''(?x)
-    ^
-    [0-9a-z]   # start with ASCII digit or lowercase
-    (
-     [0-9a-z]  # additional ASCII digit or lowercase
-     |         # -OR-
-     _[0-9a-z] # underscore and ASCII digit or lowercase
-    )*         # zero or more of OR group
-    $
-'''
-
-LINKNAME_REGEX = re.compile(LINKNAME_PATTERN)
-
 def isLinkNameFormatValid(link_name):
   """Returns True if link_name is in a valid format.
 
   Args:
     link_name: link name used in URLs for identification
   """
-  if LINKNAME_REGEX.match(link_name):
+  if path_linkname.LINKNAME_REGEX.match(link_name):
     return True
   return False
\ No newline at end of file
--- a/app/soc/views/site/docs/edit.py	Wed Oct 01 20:48:58 2008 +0000
+++ b/app/soc/views/site/docs/edit.py	Wed Oct 01 20:52:39 2008 +0000
@@ -29,8 +29,8 @@
 from django.utils.translation import ugettext_lazy
 
 from soc.logic import document
-from soc.logic import key_name
 from soc.logic import out_of_band
+from soc.logic import path_linkname
 from soc.logic.site import id_user
 from soc.views import simple
 from soc.views.helpers import custom_widgets
@@ -76,7 +76,10 @@
 
   Args:
     request: the standard django request object
-    path: the Document's site-unique "path" extracted from the URL
+    partial_path: the Document's site-unique "path" extracted from the URL,
+      minus the trailing link_name
+    link_name: the last portion of the Document's site-unique "path"
+      extracted from the URL
     template: the "sibling" template (or a search list of such templates)
       from which to construct the public.html template name (or names)
 
@@ -115,7 +118,7 @@
 
   doc = None  # assume that no Document entity will be found
 
-  path = key_name.combinePath([partial_path, linkname])
+  path = path_linkname.combinePath([partial_path, linkname])
 
   # try to fetch Document entity corresponding to path if one exists    
   try:
@@ -145,7 +148,7 @@
       if not doc:
         return http.HttpResponseRedirect('/')
 
-      new_path = key_name.combinePath([new_partial_path, new_linkname])
+      new_path = path_linkname.combinePath([new_partial_path, new_linkname])
         
       # redirect to new /site/docs/edit/new_path?s=0
       # (causes 'Profile saved' message to be displayed)
@@ -263,8 +266,7 @@
       if not doc:
         return http.HttpResponseRedirect('/')
 
-      new_path = key_name.combinePathAndLinkName(
-          new_partial_path, new_linkname)
+      new_path = path_linkname.combinePath([new_partial_path, new_linkname])
         
       # redirect to new /site/docs/edit/new_path?s=0
       # (causes 'Profile saved' message to be displayed)