*
*Here is a short update on the work I am doing on the metadata branch,
feedback is more than welcome.*
Metadata Refactor
=================
The following metadata related tickets are being addressed in my
personal metadata branch on github [0]:
#345 Metadata summary on Layer Information page assigned
#486 Add metadata records to GeoNetwork on sync and upload
#492 Pre-fill metadata form with values from GeoNetwork records
#498 Integrate metadata form with Django models
#500 Add metadata fields to Django Layer model
#501 Enable syncing of Django Layer model records to GeoNetwork
metadata records
#564 Persist metadata upon submission
The big changes happened in the ``geonode/maps`` application, mostly
in models.py following the "fat models, skinny controllers" mantra.
models.py
---------
Let's start with an explanation (and some snippets of code) about the
new entities structure. The first thing, is that the Layer/Contact
model now matches one to one the World Bank's specification (which is
based on ISO 19115:2003). Including validation of optional/required
fields.
Contact model
~~~~~~~~~~~~~
It represents a contact information object and can either be tied or
not to a geonode website user. When a contact object does not have a
user linked, then it's just an anonymous contact object, when a
contact object is attached to a user, it becomes that user's ``profile
information``. This renders unnecessary the ``geonode_profile``
application that existed in the repo.
class Contact(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
# the specification says that either name or organization should
be provided
name = models.CharField('Individual Name', blank=True, null=True)
organization = models.CharField('Organization Name', blank=True,
null=True)
# position, voice, fax, delivery, city, area, zipcode, country, email
Layer model
~~~~~~~~~~~
The layer model existed in the repo and contained mainly structures to
interact with GeoServer (workspace, store, typename) and a few with
GeoNetwork (uuid), this model has been augmented with the fields
defined in the spec (title, date, edition, constraints) and a
``ManyToMany`` field pointing at contacts. The spec defines two basic
roles: ``Point Of Contact`` and ``Metadata Author``, and the Layer
model provides accessors to those two roles. However, by using a
``ManyToMany`` relation, we can add as many contacts as we want to a
given layer, using the clearly defined ``Role`` field to specify the
type of relation.
class Layer(models.Model):
# objects,workspace,store,storeType,name,uuid,typename
contacts = models.ManyToManyField(Contact, through='ContactRole')
poc = property(_get_poc, _set_poc)
metadata_author = property(_get_metadata_author, _set_metadata_author)
# title,date,date_type,edition,abstract,purpose,maintenance_frequency
# keywords,keywords_region,constraints_use,constraints_other,
# spatial_representation_type,language,topic_category,
# temporal_extent_start,temporal_extent_end,geographic_bounding_box,
# supplemental_information,distribution_url,distribution_description,
# data_quality_statement = models.TextField(blank=True, null=True)
Role and ContactRole models
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The role model ties a given role with a sets of permissions, that way
we can detail the type of access each role should have to a given
resource, to plant the seed for a future ``per-object role based
permission system``. The ContactRole model is used as an intermediary
table on the ManyToMany relationship between layers and contacts. More
information about this structure can be found in Django Docs [1]
ROLE_VALUES = [
'datasetProvider',
'custodian',
'owner',
'user',
'distributor',
'originator',
'pointOfContact',
'principalInvestigator',
'processor',
'publisher',
'author'
]
class Role(models.Model):
value = models.CharField('Role', choices= ROLE_VALUES, unique=True)
permissions = models.ManyToManyField(Permission)
class ContactRole(models.Model):
contact = models.ForeignKey(Contact)
layer = models.ForeignKey(Layer)
role = models.ForeignKey(Role)
Layer.slurp
~~~~~~~~~~~
The slurp method now gets more information from GeoNetwork (bbox,
keywords, distribution url) and sets the ``poc`` and
``metadata_author`` attributes.
Layer.save_to_geonetwork and Layer.save_to_geoserver
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
These ugly named methods serialize the relevant information in the
layer object and push it to the relevant server (GeoServer or
GeoNetwork). It is hooked to the post_save signal of the Layer model.
Layer.delete_from_geoserver and Layer.delete_from_geonetwork
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Similar to the ones described above, it is hooked to the pre_delete
signal of the Layer model.
forms.py
--------
Since all the information is now baked in the models, the ContactForm
and MetadataForm are no longer needed, they are automatically created
using the form_from_model function Django provides.
views.py
--------
A lot of the initialization code from GeoNode and custom form handling
was removed, the view that serves "describe layer" is now very slim
and saves both the layer form as well as the poc and metadata_author ones.
settings.py
-----------
'registration' and 'profiles' are now part of ``geonode/settings.py``,
the profile module is now set to be ``maps.Contact`` to allow both
anonymous contacts and users with contact information as explained above.
AUTH_PROFILE_MODULE = 'maps.Contact'
Running the branch
==================
I am going to copy/paste/tweak David's notes about running a branch
from a different repo below:
# add 'ingenieroariel' as an alias for my github fork.
# the '-f' means "go ahead and fetch from that repo now"
git remote add ingenieroariel
git://github.com/ingenieroariel/geonode.git
<http://github.com/ingenieroariel/geonode.git> -f
# create a local branch (checkout -b) that you can work on that tracks
# my github repo (eg, uses it for pull and merge)
git checkout ingenieroariel/metadata -b metadata --track
I plan to create a patch against the current master to make it easier
to review the code changes, I'll do that today after doing the merge
with last week's geonode development.
Ariel.
[0] http://github.com/ingenieroariel/geonode/tree/metadata
[1]
http://docs.djangoproject.com/en/1.2/topics/db/models/#intermediary-manytomany*