Skip to content

Commit

Permalink
[merge] from default
Browse files Browse the repository at this point in the history
  • Loading branch information
amercader committed Jul 28, 2011
2 parents ce88373 + 116dd07 commit 6b1f372
Show file tree
Hide file tree
Showing 43 changed files with 955 additions and 164 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.txt
Expand Up @@ -5,7 +5,19 @@ v1.4.2 2011-XX-XX
=================
Major:
* Packages revisions can be marked as 'moderated' (#1141)
* Password reset facility (#1186/#1198)

Minor:
* Viewing of a package at any revision (#1141)
* API POSTs can be of Content-Type "application/json" as alternative to existing "application/x-www-form-urlencoded" (#1206)
* Caching of static files (#1223)

Bug fixes:
* When you removed last row of resource table, you could't add it again - since 1.0 (#1215)
* Exception if you had any Groups and migrated between CKAN v1.0.2 to v1.2 (migration 29) - since v1.0.2 (#1205)
* API Package edit requests returned the Package in a different format to usual - since 1.4 (#1214)
* API error responses were not all JSON format and didn't have correct Content-Type (#1214)
* API package delete doesn't require a Content-Length header (#1214)


v1.4.1 2011-06-27
Expand Down
5 changes: 5 additions & 0 deletions README.txt
Expand Up @@ -371,6 +371,11 @@ Common problems running tests

python -c "import pylons"

* `OperationalError: (OperationalError) no such function: plainto_tsquery ...`

This error usually results from running a test which involves search functionality, which requires using a PostgreSQL database, but another (such as SQLite) is configured. The particular test is either missing a `@search_related` decorator or there is a mixup with the test configuration files leading to the wrong database being used.


Testing extensions
------------------

Expand Down
12 changes: 10 additions & 2 deletions ckan/ckan_nose_plugin.py
Expand Up @@ -4,8 +4,7 @@
import sys
import pkg_resources
from paste.deploy import loadapp

pylonsapp = None
from pylons import config

class CkanNose(Plugin):
settings = None
Expand All @@ -29,6 +28,15 @@ def startContext(self, ctx):
# the db is destroyed after every test when you Session.Remove().
model.repo.init_db()

## This is to make sure the configuration is run again.
## Plugins use configure to make their own tables and they
## may need to be recreated to make tests work.
from ckan.plugins import PluginImplementations
from ckan.plugins.interfaces import IConfigurable
for plugin in PluginImplementations(IConfigurable):
plugin.configure(config)


def options(self, parser, env):
parser.add_option(
'--ckan',
Expand Down
40 changes: 9 additions & 31 deletions ckan/controllers/api.py
Expand Up @@ -11,6 +11,7 @@
from ckan.plugins import PluginImplementations, IGroupController
from ckan.lib.munge import munge_title_to_name
from ckan.lib.navl.dictization_functions import DataError
from ckan.logic import get_action
import ckan.logic.action.get as get
import ckan.logic.action.create as create
import ckan.logic.action.update as update
Expand Down Expand Up @@ -129,32 +130,9 @@ def get_api(self, ver=None):
response_data = {}
response_data['version'] = ver or '1'
return self._finish_ok(response_data)

@classmethod
def create_actions(cls):
if cls._actions:
return
for name, action in get.__dict__.iteritems():
if not name.startswith('_') and callable(action):
cls._actions[name] = action
for name, action in update.__dict__.iteritems():
if not name.startswith('_') and callable(action):
cls._actions[name] = action
for name, action in create.__dict__.iteritems():
if not name.startswith('_') and callable(action):
cls._actions[name] = action

def get_action(self, action):
self.create_actions()
return self._actions[action]

@classmethod
def register_action(cls, name, function):
cls.create_actions()
cls._actions[name] = function

def action(self, logic_function):
function = self.get_action(logic_function)
function = get_action(logic_function)

context = {'model': model, 'session': model.Session, 'user': c.user}
model.Session()._context = context
Expand Down Expand Up @@ -259,10 +237,10 @@ def _represent_package(self, package):
def create(self, ver=None, register=None, subregister=None, id=None, id2=None):

action_map = {
('package', 'relationships'): create.package_relationship_create,
'group': create.group_create_rest,
'package': create.package_create_rest,
'rating': create.rating_create,
('package', 'relationships'): get_action('package_relationship_create'),
'group': get_action('group_create_rest'),
'package': get_action('package_create_rest'),
'rating': get_action('rating_create'),
}

for type in model.PackageRelationship.get_all_types():
Expand Down Expand Up @@ -309,9 +287,9 @@ def create(self, ver=None, register=None, subregister=None, id=None, id2=None):
def update(self, ver=None, register=None, subregister=None, id=None, id2=None):

action_map = {
('package', 'relationships'): update.package_relationship_update,
'package': update.package_update_rest,
'group': update.group_update_rest,
('package', 'relationships'): get_action('package_relationship_update'),
'package': get_action('package_update_rest'),
'group': get_action('group_update_rest'),
}
for type in model.PackageRelationship.get_all_types():
action_map[('package', type)] = update.package_relationship_update
Expand Down
5 changes: 3 additions & 2 deletions ckan/controllers/home.py
Expand Up @@ -43,7 +43,8 @@ def index(self):

query = query_for(model.Package)
query.run(query='*:*', facet_by=g.facets,
limit=0, offset=0, username=c.user)
limit=0, offset=0, username=c.user,
order_by=None)
c.facets = query.facets
c.fields = []
c.package_count = query.count
Expand Down Expand Up @@ -74,7 +75,7 @@ def locale(self):
abort(400, _('Invalid language specified'))
h.flash_notice(_("Language has been set to: English"))
else:
h.flash_notice(_("No language given!"))
abort(400, _("No language given!"))
return_to = get_redirect()
if not return_to:
# no need for error, just don't redirect
Expand Down
32 changes: 20 additions & 12 deletions ckan/controllers/package.py
Expand Up @@ -15,6 +15,7 @@
import ckan.logic.action.create as create
import ckan.logic.action.update as update
import ckan.logic.action.get as get
from ckan.logic import get_action
from ckan.logic.schema import package_form_schema
from ckan.lib.base import request, c, BaseController, model, abort, h, g, render
from ckan.lib.base import etag_cache, response, redirect, gettext
Expand Down Expand Up @@ -173,24 +174,30 @@ def _pkg_cache_key(pkg):
# revision may have more than one package in it.
return str(hash((pkg.id, pkg.latest_related_revision.id, c.user, pkg.get_average_rating())))

def _clear_pkg_cache(self, pkg):
read_cache = cache.get_cache('package/read.html', type='dbm')
read_cache.remove_value(self._pkg_cache_key(pkg))

@proxy_cache()
def read(self, id):
context = {'model': model, 'session': model.Session,
'user': c.user or c.author, 'extras_as_string': True,
'schema': self._form_to_db_schema()}
data_dict = {'id': id}

# interpret @<revision_id> or @<date> suffix
split = id.split('@')
if len(split) == 2:
data_dict['id'], revision = split
try:
date = datetime.datetime(*map(int, re.split('[^\d]', revision)))
context['revision_date'] = date
except ValueError:
context['revision_id'] = revision
data_dict['id'], revision_ref = split
if model.is_id(revision_ref):
context['revision_id'] = revision_ref
else:
try:
date = model.strptimestamp(revision_ref)
context['revision_date'] = date
except TypeError, e:
abort(400, _('Invalid revision format: %r') % e.args)
except ValueError, e:
abort(400, _('Invalid revision format: %r') % e.args)
elif len(split) > 2:
abort(400, _('Invalid revision format: %r') % 'Too many "@" symbols')

#check if package exists
try:
c.pkg_dict = get.package_show(context, data_dict)
Expand Down Expand Up @@ -437,7 +444,7 @@ def _save_new(self, context):
tuplize_dict(parse_params(request.POST))))
self._check_data_dict(data_dict)
context['message'] = data_dict.get('log_message', '')
pkg = create.package_create(context, data_dict)
pkg = get_action('package_create')(context, data_dict)

if context['preview']:
PackageSaver().render_package(pkg, context)
Expand Down Expand Up @@ -468,7 +475,7 @@ def _save_edit(self, id, context):
if not context['moderated']:
context['pending'] = False
data_dict['id'] = id
pkg = update.package_update(context, data_dict)
pkg = get_action('package_update')(context, data_dict)
if request.params.get('save', '') == 'Approve':
update.make_latest_pending_package_active(context, data_dict)
c.pkg = context['package']
Expand Down Expand Up @@ -522,6 +529,7 @@ def authz(self, id):
pkg = model.Package.get(id)
if pkg is None:
abort(404, gettext('Package not found'))
c.pkg = pkg # needed to add in the tab bar to the top of the auth page
c.pkgname = pkg.name
c.pkgtitle = pkg.title

Expand Down
2 changes: 2 additions & 0 deletions ckan/controllers/user.py
Expand Up @@ -216,6 +216,8 @@ def _save_edit(self, id, context):


def login(self):
if 'error' in request.params:
h.flash_error(request.params['error'])
return render('user/login.html')

def logged_in(self):
Expand Down
11 changes: 9 additions & 2 deletions ckan/lib/dictization/model_dictize.py
Expand Up @@ -78,7 +78,11 @@ def resource_dictize(res, context):
return resource

def _execute_with_revision(q, rev_table, context):
'''
Raises NotFound if the context['revision_id'] does not exist.
Returns [] if there are no results.
'''
model = context['model']
meta = model.meta
session = model.Session
Expand All @@ -87,8 +91,11 @@ def _execute_with_revision(q, rev_table, context):
pending = context.get('pending')

if revision_id:
revision_date = session.query(context['model'].Revision).filter_by(
id=revision_id).one().timestamp
revision = session.query(context['model'].Revision).filter_by(
id=revision_id).first()
if not revision:
raise NotFound
revision_date = revision.timestamp

if revision_date:
q = q.where(rev_table.c.revision_timestamp <= revision_date)
Expand Down
18 changes: 9 additions & 9 deletions ckan/lib/dictization/model_save.py
Expand Up @@ -5,17 +5,15 @@
##package saving

def resource_dict_save(res_dict, context):

model = context["model"]
session = context["session"]

obj = None

# try to get resource object directly from context, then by ID
# if not found, create a new resource object
id = res_dict.get("id")

if id:
obj = context.get("resource")
if (not obj) and id:
obj = session.query(model.Resource).get(id)

if not obj:
obj = model.Resource()

Expand All @@ -30,14 +28,17 @@ def resource_dict_save(res_dict, context):
if key in fields:
setattr(obj, key, value)
else:
# resources save extras directly onto the object, instead
# of in a separate extras field like packages and groups
obj.extras[key] = value

if context.get('pending'):
if session.is_modified(obj, include_collections=False):
obj.state = 'pending'
obj.state = u'pending'
else:
obj.state = u'active'

session.add(obj)

return obj

def package_resource_list_save(res_dicts, package, context):
Expand Down Expand Up @@ -126,7 +127,6 @@ def group_extras_save(extras_dicts, context):
return result_dict

def package_tag_list_save(tag_dicts, package, context):


allow_partial_update = context.get("allow_partial_update", False)
if not tag_dicts and allow_partial_update:
Expand Down
18 changes: 15 additions & 3 deletions ckan/lib/helpers.py
Expand Up @@ -204,10 +204,22 @@ def pager(self, *args, **kwargs):


def render_datetime(datetime_):
'''Render a datetime object as a string in a reasonable way (Y-m-d H:m).
'''Render a datetime object or timestamp string as a pretty string
(Y-m-d H:m).
If timestamp is badly formatted, then a blank string is returned.
'''
if datetime_:
return datetime_.strftime('%Y-%m-%d %H:%M')
from ckan import model
date_format = '%Y-%m-%d %H:%M'
if isinstance(datetime_, datetime):
return datetime_.strftime(date_format)
elif isinstance(datetime_, basestring):
try:
datetime_ = model.strptimestamp(datetime_)
except TypeError:
return ''
except ValueError:
return ''
return datetime_.strftime(date_format)
else:
return ''

Expand Down

0 comments on commit 6b1f372

Please sign in to comment.