Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Sync => modules

PE affiliation - basic data model
  • Loading branch information...
commit b72637ec716c686933fd155019880cbe50a8f08b 1 parent 941c941
@nursix nursix authored committed
View
2  .gitattributes
@@ -1 +1 @@
-VERSION merge=keeplocal filter=version
+VERSION merge=keeplocal
View
2  VERSION
@@ -1 +1 @@
-r3189 (2012-02-08 21:50:37)
+1a3cd6a (2012-02-09 13:07:19)
View
12 controllers/pr.py
@@ -315,6 +315,18 @@ def pentity():
return s3_rest_controller()
# -----------------------------------------------------------------------------
+def affiliation():
+ """ RESTful CRUD controller """
+
+ return s3_rest_controller()
+
+# -----------------------------------------------------------------------------
+def role():
+ """ RESTful CRUD controller """
+
+ return s3_rest_controller()
+
+# -----------------------------------------------------------------------------
def tooltip():
""" Ajax tooltips """
View
5 controllers/sync.py
@@ -51,8 +51,7 @@ def repository():
tabs = [(T("Configuration"), None),
(T("Resources"), "task"),
(T("Schedule"), "job"),
- (T("Log"), "log"),
- (T("Manual Sync"), "now")
+ (T("Log"), "log")
]
s3mgr.model.set_method("sync", "repository",
@@ -85,7 +84,7 @@ def postp(r, output):
return output
response.s3.postp = postp
- rheader = lambda r: sync_rheader(r, tabs=tabs)
+ rheader = lambda r: s3db.sync_rheader(r, tabs=tabs)
output = s3_rest_controller(prefix, resourcename, rheader=rheader)
return output
View
2  models/00_tables.py
@@ -37,6 +37,7 @@
import eden.support
import eden.survey
import eden.hms
+import eden.sync
#import eden.patient
# =============================================================================
@@ -238,6 +239,7 @@ def s3_meta_fields():
scheduler_task_id = S3ReusableField("scheduler_task_id",
"reference %s" % s3base.S3Task.TASK_TABLENAME,
ondelete="CASCADE")
+s3.scheduler_task_id = scheduler_task_id
# =============================================================================
# Reusable roles fields for map layer permissions management (GIS)
View
618 models/sync.py
@@ -1,618 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
- Synchronization, Model
-
- @author: Dominic König <dominic[at]aidiq[dot]com>
-"""
-
-def sync_tables():
- """ Synchronization tables loader """
-
- if "sync_config" in db:
- return
-
- # -------------------------------------------------------------------------
- # Configuration
- # -------------------------------------------------------------------------
- tablename = "sync_config"
- table = db.define_table(tablename,
- Field("proxy",
- label=T("Proxy Server URL"),
- requires=IS_EMPTY_OR(
- IS_URL(mode="generic"))),
- *s3_meta_fields())
-
- table.uuid.readable = True
- table.uuid.label = "UUID"
-
- table.uuid.comment = DIV(_class="tooltip",
- _title="%s|%s" % (
- T("UUID"),
- T("Unique identifier which THIS repository identifies itself with when sending synchronization requests.")))
- table.proxy.comment = DIV(_class="tooltip",
- _title="%s|%s" % (
- T("Proxy Server URL"),
- T("URL of the default proxy server to connect to remote repositories (if required). If only some of the repositories require the use of a proxy server, you can configure this in the respective repository configurations.")))
-
- # CRUD strings
- s3.crud_strings[tablename] = Storage(
- title_display = T("Synchronization Settings"),
- title_update = T("Edit Synchronization Settings"),
- msg_record_modified = T("Synchronization settings updated"))
-
- # CRUD configuration
- s3mgr.configure(tablename,
- insertable=False,
- deletable=False,
- update_next=URL(c="sync", f="config",
- args=["1", "update"]))
-
- # -------------------------------------------------------------------------
- # Status
- # -------------------------------------------------------------------------
- tablename = "sync_status"
- table = db.define_table(tablename,
- Field("running", "boolean",
- default=False,
- readable=False,
- writable=False),
- Field("manual", "boolean",
- default=False,
- readable=False,
- writable=False),
- Field("timestmp", "datetime",
- readable=False,
- writable=False))
-
- # -------------------------------------------------------------------------
- # Repository
- # -------------------------------------------------------------------------
- tablename = "sync_repository"
- table = db.define_table(tablename,
- Field("name",
- length=64,
- notnull=True),
- Field("url",
- label="URL"),
- Field("username"),
- Field("password", "password"),
- Field("proxy",
- label=T("Proxy Server URL"),
- requires=IS_EMPTY_OR(
- IS_URL(mode="generic"))),
- Field("last_status",
- readable=False,
- writable=False,
- label=T("Last status")),
- Field("accept_push", "boolean",
- default=False,
- label=T("Accept Push")),
- *s3_meta_fields())
-
- table.uuid.readable = True
- table.uuid.writable = True
- table.uuid.label = "UUID"
-
- table.url.requires = IS_EMPTY_OR(IS_NOT_IN_DB(db, "sync_repository.url"))
-
- table.name.comment = DIV(_class="tooltip",
- _title="%s|%s" % (
- T("Repository Name"),
- T("Name of the repository (for you own reference)")))
- table.url.comment = DIV(_class="tooltip",
- _title="%s|%s" % (
- T("Repository Base URL"),
- T("Base URL of the remote Sahana Eden instance including application path, e.g. http://www.example.org/eden")))
- table.proxy.comment = DIV(_class="tooltip",
- _title="%s|%s" % (
- T("Proxy Server URL"),
- T("URL of the proxy server to connect to the repository (leave empty for default proxy)")))
- table.username.comment = DIV(_class="tooltip",
- _title="%s|%s" % (
- T("Username"),
- T("Username to use for authentication at the remote site.")))
- table.password.comment = DIV(_class="tooltip",
- _title="%s|%s" % (
- T("Password"),
- T("Password to use for authentication at the remote site.")))
- table.uuid.comment = DIV(_class="tooltip",
- _title="%s|%s" % (
- T("Repository UUID"),
- T("Identifier which the repository identifies itself with when sending synchronization requests.")))
- table.accept_push.comment = DIV(_class="tooltip",
- _title="%s|%s" % (
- T("Accept Push"),
- T("Accept unsolicited data transmissions from the repository.")))
-
- # CRUD strings
- ADD_REPOSITORY = T("Add Repository")
- s3.crud_strings[tablename] = Storage(
- title_create = ADD_REPOSITORY,
- title_display = T("Repository Configuration"),
- title_list = T("Repositories"),
- title_update = T("Edit Repository Configuration"),
- title_search = T("Search for Repository"),
- subtitle_create = T("Add Repository"),
- subtitle_list = T("Currently Configured Repositories"),
- label_list_button = T("List Repositories"),
- label_create_button = ADD_REPOSITORY,
- msg_record_created = T("Repository configured"),
- msg_record_modified = T("Repository configuration updated"),
- msg_record_deleted = T("Repository configuration deleted"),
- msg_list_empty = T("No repositories configured"))
-
- # -------------------------------------------------------------------------
- # Repository representation
- def sync_repository_represent(rid):
-
- rtable = db.sync_repository
- repository = db(rtable.id == rid).select(rtable.name,
- limitby=(0, 1)).first()
- if repository:
- return repository.name
- else:
- return NONE
-
- # -------------------------------------------------------------------------
- # Reusable repository_id
- repository_id = S3ReusableField("repository_id", db.sync_repository,
- requires = IS_ONE_OF(db,
- "sync_repository.id",
- "%(name)s"),
- represent = sync_repository_represent,
- label = T("Repository"))
-
- # -------------------------------------------------------------------------
- def sync_repository_ondelete(row):
- """
- Cleanup after repository deletion
-
- @todo: use standard delete cascade
- """
-
- # Delete all resources in this repository
- rtable = db.sync_resource
- db(rtable.repository_id == row.id).update(deleted=True)
-
- # Delete all jobs for this repository
- # @todo: remove scheduler_task entry as well
- jtable = db.sync_job
- db(jtable.repository_id == row.id).update(deleted=True)
-
- # Delete all pending conflicts of this repository
- ctable = db.sync_conflict
- db(ctable.repository_id == row.id).delete()
-
- # Delete all log entries for this repository
- ltable = db.sync_log
- db(ltable.repository_id == row.id).delete()
-
- # -------------------------------------------------------------------------
- def sync_repository_onaccept(form):
- """
- Send registration request to the peer
- """
-
- try:
- repository_id = form.vars.id
- except:
- return
-
- if repository_id:
- rtable = db.sync_repository
- query = rtable.id == repository_id
- repository = db(query).select(limitby=(0, 1)).first()
- if repository and repository.url:
- sync = current.manager.sync
- success = sync.request_registration(repository)
- if not success:
- response.warning = T("Could not auto-register at the repository, please register manually.")
- else:
- response.confirmation = T("Successfully registered at the repository.")
- return
-
- # -------------------------------------------------------------------------
- # Repository virtual list fields
- class SyncRepositoryVirtualFields:
- def last_sync_time(self):
- table = db.sync_task
- query = table.repository_id == self.sync_repository.id
- task = db(query).select(orderby=~table.last_sync,
- limitby=(0,1)).first()
- if task:
- return s3_datetime_represent(task.last_sync, utc=True)
- else:
- return T("never")
- table.virtualfields.append(SyncRepositoryVirtualFields())
-
- # -------------------------------------------------------------------------
- # Repository table configuration
- s3mgr.configure(tablename,
- list_fields=["name",
- "uuid",
- "accept_push",
- (T("Last Synchronization"),
- "last_sync_time")],
- onaccept=sync_repository_onaccept,
- ondelete=sync_repository_ondelete,
- create_next=URL(c="sync", f="repository",
- args=["[id]", "task"]),
- update_next=URL(c="sync", f="repository",
- args=["[id]"]))
-
- # -------------------------------------------------------------------------
- # Task
- # -------------------------------------------------------------------------
- # Synchronization mode
- sync_mode = {
- 1: T("pull"), # pull only
- 2: T("push"), # push only
- 3: T("pull and push"), # pull & push
- 4: T("none") # do not synchronize this resource
- }
-
- # Strategy (allowed import methods)
- sync_strategy = s3base.S3ImportItem.METHOD
-
- sync_strategy_represent = lambda opt: opt and \
- ", ".join([o for o in sync_strategy.values()
- if o in opt]) or NONE
-
- # Update method
- sync_update_method = {
- 1: T("update"), # update the existing record
- 2: T("replace"), # replace the existing record
- }
-
- # Update/conflict resolution policy
- sync_policies = s3base.S3ImportItem.POLICY
- sync_policy = {
- sync_policies.OTHER: T("always update"),
- sync_policies.NEWER: T("update if newer"),
- sync_policies.MASTER: T("update if master"),
- sync_policies.THIS: T("never update")
- }
-
- sync_policy_represent = lambda opt: \
- opt and sync_policy.get(opt, UNKNOWN_OPT) or NONE
-
- tablename = "sync_task"
- table = db.define_table(tablename,
- Field("resource_name",
- notnull=True),
- repository_id(),
- Field("last_sync", "datetime",
- readable=True,
- writable=False,
- update="",
- label=T("Last synchronized on")),
- Field("mode", "integer",
- requires = IS_IN_SET(sync_mode,
- zero=None),
- default = 3,
- label = T("Mode"),
- represent = lambda opt: \
- sync_mode.get(opt, NONE)),
-
- Field("strategy", "list:string",
- requires = IS_IN_SET(sync_strategy.values(),
- multiple=True,
- zero=None),
- default = sync_strategy.values(),
- label = T("Strategy"),
- represent = sync_strategy_represent,
- widget = CheckboxesWidgetS3.widget),
-
- Field("update_method", "integer",
- # hide while not implemented
- readable=False,
- writable=False,
- requires = IS_IN_SET(sync_update_method,
- zero=None),
- default = 1,
- label = T("Update Method"),
- represent = lambda opt: \
- sync_update_method.get(opt,
- NONE)),
-
- Field("update_policy",
- requires = IS_IN_SET(sync_policies,
- zero=None),
- default = sync_policies.NEWER,
- label = T("Update Policy"),
- represent = sync_policy_represent),
-
- Field("conflict_policy",
- requires = IS_IN_SET(sync_policies,
- zero=None),
- default = sync_policies.NEWER,
- label = T("Conflict Policy"),
- represent = sync_policy_represent),
-
- *s3_meta_fields())
-
- table.resource_name.comment = DIV(_class="tooltip",
- _title="%s|%s" % (
- T("Resource Name"),
- T("Table name of the resource to synchronize")))
-
- table.mode.comment = DIV(_class="tooltip",
- _title="%s|%s" % (
- T("Synchronization mode"),
- T("How data shall be transferred")))
- table.strategy.comment = DIV(_class="tooltip",
- _title="%s|%s" % (
- T("Strategy"),
- T("Which methods to apply when importing data to the local repository")))
- table.update_method.comment = DIV(_class="tooltip",
- _title="%s|%s" % (
- T("Update Method"),
- T("How local records shall be updated")))
- table.update_policy.comment = DIV(_class="tooltip",
- _title="%s|%s" % (
- T("Update Policy"),
- T("Under which conditions local records shall be updated")))
- table.conflict_policy.comment = DIV(_class="tooltip",
- _title="%s|%s" % (
- T("Conflict policy"),
- T("Under which condition a local record shall be updated if it also has been modified locally since the last synchronization")))
-
- # CRUD strings
- ADD_TASK = T("Add Resource")
- s3.crud_strings[tablename] = Storage(
- title_create = ADD_TASK,
- title_display = T("Resource Configuration"),
- title_list = T("Resources"),
- title_update = T("Edit Resource Configuration"),
- title_search = T("Search for Resource"),
- subtitle_create = ADD_TASK,
- subtitle_list = T("Currently Configured Resources"),
- label_list_button = T("List Resources"),
- label_create_button = ADD_TASK,
- msg_record_created = T("Resource configured"),
- msg_record_modified = T("Resource configuration updated"),
- msg_record_deleted = T("Resource configuration deleted"),
- msg_list_empty = T("No resources configured yet"))
-
- # -------------------------------------------------------------------------
- def sync_task_onvalidation(form):
-
- repository_id = form.vars.repository_id or \
- request.post_vars.repository_id
- resource_name = form.vars.resource_name
-
- if repository_id and resource_name:
- ttable = db.sync_task
- query = (ttable.repository_id == repository_id) & \
- (ttable.resource_name == resource_name) & \
- (ttable.deleted != True)
- row = db(query).select(ttable.id, limitby=(0, 1)).first()
- if row:
- form.errors.resource_name = \
- T("This resource is already configured for this repository")
-
- s3mgr.configure(tablename,
- create_onvalidation=sync_task_onvalidation)
-
- # -------------------------------------------------------------------------
- # Job
- # -------------------------------------------------------------------------
- tablename = "sync_job"
- table = db.define_table(tablename,
- repository_id(),
- scheduler_task_id())
-
- # CRUD strings
- ADD_JOB = T("Add Job")
- s3.crud_strings[tablename] = Storage(
- title_create = ADD_JOB,
- title_display = T("Synchronization Job"),
- title_list = T("Synchronization Schedule"),
- title_update = T("Edit Job"),
- title_search = T("Search for Job"),
- subtitle_create = ADD_JOB,
- subtitle_list = T("Currently Configured Jobs"),
- label_list_button = T("List Jobs"),
- label_create_button = ADD_JOB,
- msg_record_created = T("Job added"),
- msg_record_modified = T("Job updated updated"),
- msg_record_deleted = T("Job deleted"),
- msg_list_empty = T("No jobs configured yet"),
- msg_no_match = T("No jobs configured"))
-
- # -------------------------------------------------------------------------
- def sync_job_reset(r, **attr):
- """
- RESTful method to reset a job status from FAILED to QUEUED, for
- "Reset" action button
- """
-
- if r.interactive:
- if r.component and r.component.alias == "job":
- job_id = r.component_id
- if job_id:
- s3base.S3Task.reset(job_id)
- session.confirmation = T("Job reactivated")
- r.component_id = None
- redirect(r.url(method=""))
-
- # Add as method for the "job" component
- s3mgr.model.set_method("sync", "repository",
- component_name="job",
- method="reset",
- action=sync_job_reset)
-
- # -------------------------------------------------------------------------
- # Conflicts
- # -------------------------------------------------------------------------
- tablename = "sync_conflict"
- table = db.define_table(tablename,
- repository_id(),
- Field("dummy"))
- # @todo: implement table
- # @todo: CRUD strings
-
- s3mgr.configure(tablename,
- insertable=False,
- editable=False)
-
- # -------------------------------------------------------------------------
- # Log
- # -------------------------------------------------------------------------
- tablename = "sync_log"
- table = db.define_table(tablename,
- Field("timestmp", "datetime",
- represent=lambda dt: \
- s3_datetime_represent(dt, utc=True),
- label=T("Date/Time")),
- repository_id(),
- Field("resource_name"),
- # Synchronization mode: PULL/PUSH, IN/OUT
- Field("mode"),
- Field("action"),
- Field("result"),
- Field("remote", "boolean",
- default=False,
- label=T("Remote Error"),
- represent=lambda opt: opt and T("yes") or ("no")),
- Field("message", "text"),
- *s3_meta_fields())
-
- # CRUD strings
- s3.crud_strings[tablename] = Storage(
- title_display = T("Log Entry"),
- title_list = T("Synchronization Log"),
- subtitle_list = T("Synchronization Log"),
- label_list_button = T("List All Entries"),
- msg_record_deleted = T("Log Entry Deleted"),
- msg_list_empty = T("No entries found"),
- msg_no_match = T("No entries found"))
-
- # Configuration
- s3mgr.configure(tablename,
- editable=False,
- insertable=False,
- deletable=True,
- orderby=~table.timestmp)
-
- # -------------------------------------------------------------------------
-
-# Component definitions
-s3mgr.model.add_component("sync_task",
- sync_repository="repository_id")
-
-s3mgr.model.add_component(s3base.S3Task.TASK_TABLENAME,
- sync_repository=dict(name="job",
- joinby="repository_id",
- link="sync_job",
- key="scheduler_task_id",
- actuate="replace"))
-
-s3mgr.model.add_component("sync_log",
- sync_repository="repository_id")
-
-s3mgr.model.add_component("sync_conflict",
- sync_repository="repository_id")
-
-# Loader configuration
-s3mgr.model.loader(sync_tables,
- "sync_config",
- "sync_status",
- "sync_repository",
- "sync_resource",
- "sync_job",
- "sync_conflict",
- "sync_log")
-
-# -----------------------------------------------------------------------------
-def sync_rheader(r, tabs=[]):
- """
- Synchronization resource headers
- """
-
- T = current.T
-
- if r.representation == "html":
-
- if r.tablename == "sync_repository":
- repository = r.record
- if r.component and r.component_name=="log" and not r.component_id:
- purge_log = A(T("Remove all log entries"),
- _href=r.url(method="delete"))
- else:
- purge_log = ""
- if repository:
- if repository.url:
- tabs.append((T("Manual Synchronization"), "now"))
- rheader_tabs = s3_rheader_tabs(r, tabs)
- rheader = DIV(TABLE(
- TR(TH("%s: " % T("Name")),
- repository.name,
- TH(""),
- purge_log),
- TR(TH("URL: "),
- repository.url,
- TH(""),
- ""),
- ), rheader_tabs)
- return rheader
-
- return None
-
-# -----------------------------------------------------------------------------
-def sync_now(r, **attr):
- """
- Manual synchronization of a repository
- """
-
- manager = current.manager
-
- rheader = attr.get("rheader", None)
- if rheader:
- rheader = rheader(r)
-
- output = dict(title=T("Manual Synchronization"), rheader=rheader)
- s3task = current.s3task
-
- sync = s3base.S3Sync()
-
- if r.interactive:
- if r.http in ("GET", "POST"):
- repository = r.record
- if not repository:
- r.error(404, manager.ERROR.BAD_RECORD)
- form = FORM(TABLE(
- TR(TD(T("Click 'Start' to synchronize with this repository now:"))),
- TR(TD(INPUT(_type="submit", _value=T("Start"))))))
- if form.accepts(r.post_vars, session):
- task_id = s3task.async("sync_synchronize",
- args = [repository.id],
- vars = dict(user_id=auth.user.id,
- manual=True))
- if task_id is False:
- response.error = T("Could not initiate manual synchronization.")
- elif task_id is None:
- response.flash = T("Manual synchronization completed.")
- else:
- sync.set_status(manual=True)
- response.flash = T("Manual synchronization started in the background.")
- else:
- r.error(405, manager.ERROR.BAD_METHOD)
- else:
- r.error(501, manager.ERROR.BAD_FORMAT)
-
- status = sync.get_status()
- if status.running:
- output.update(form=T("Synchronization currently active - refresh page to update status."))
- elif not status.manual:
- output.update(form=form)
- else:
- output.update(form=T("Manual synchronization scheduled - refresh page to update status."))
-
- response.view = "update.html"
- return output
-
-s3mgr.model.set_method("sync", "repository", method="now", action=sync_now)
-
-# END =========================================================================
View
13 modules/eden/org.py
@@ -395,6 +395,17 @@ def model(self):
# Components
+ # Affiliates
+ self.add_component("pr_affiliation",
+ org_organisation=Storage(name="affiliate",
+ pkey="pe_id",
+ joinby="parent"))
+
+ # Affiliation
+ self.add_component("pr_affiliation",
+ org_organisation=Storage(pkey="pe_id",
+ joinby="child"))
+
# Staff
self.add_component("hrm_human_resource",
org_organisation="organisation_id")
@@ -1254,7 +1265,7 @@ def org_rheader(r, tabs=[]):
website,
sectors,
), rheader_tabs)
-
+
elif tablename == "org_office":
s3 = current.response.s3
View
70 modules/eden/pr.py
@@ -53,12 +53,15 @@ class S3PersonEntity(S3Model):
""" Person Super-Entity """
names = ["pr_pentity",
+ "pr_affiliation",
+ "pr_role",
"pr_pe_label"]
def model(self):
db = current.db
T = current.T
+ s3 = current.response.s3
pe_types = Storage(pr_person = T("Person"),
pr_group = T("Group"),
@@ -66,6 +69,9 @@ def model(self):
org_office = T("Office"),
dvi_body = T("Body"))
+ # ---------------------------------------------------------------------
+ # Person Super-Entity
+ #
tablename = "pr_pentity"
table = self.super_entity(tablename, "pe_id", pe_types,
Field("pe_label", length=128))
@@ -118,12 +124,76 @@ def model(self):
multiple=False))
# ---------------------------------------------------------------------
+ # Affiliation link table
+ #
+ tablename = "pr_affiliation"
+ table = self.define_table(tablename,
+ Field("hierarchy"),
+ Field("parent",
+ "reference pr_pentity",
+ requires = IS_ONE_OF(db, "pr_pentity.pe_id",
+ pr_pentity_represent,
+ sort=True),
+ represent = pr_pentity_represent),
+ Field("child",
+ "reference pr_pentity",
+ requires = IS_ONE_OF(db, "pr_pentity.pe_id",
+ pr_pentity_represent,
+ sort=True),
+ represent = pr_pentity_represent),
+ *s3.meta_fields())
+
+ # ---------------------------------------------------------------------
+ # Role
+ #
+ tablename = "pr_role"
+ table = self.define_table(tablename,
+ self.super_link("pe_id", "pr_pentity",
+ readable=True,
+ writable=True),
+ Field("hierarchy"),
+ Field("role"),
+ Field("affiliation",
+ "reference pr_affiliation",
+ requires = IS_EMPTY_OR(IS_ONE_OF(db,
+ "pr_affiliation.id",
+ self.pr_affiliation_represent,
+ sort=True)),
+ represent = self.pr_affiliation_represent),
+ *s3.meta_fields())
+
+ table.pe_id.requires = IS_ONE_OF(db, "pr_pentity.pe_id",
+ pr_pentity_represent, sort=True)
+ table.pe_id.represent = pr_pentity_represent
+
+ # ---------------------------------------------------------------------
# Return model-global names to response.s3
#
return Storage(
pr_pe_label=pr_pe_label,
)
+ # -------------------------------------------------------------------------
+ @staticmethod
+ def pr_affiliation_represent(affiliation_id):
+
+ db = current.db
+ s3db = current.s3db
+
+ if isinstance(affiliation_id, Row):
+ affiliation = affiliation_id
+ else:
+ atable = s3db.pr_affiliation
+ query = (atable.deleted != True) & \
+ (atable.id == affiliation_id)
+ affiliation = db(query).select(atable.parent, atable.child,
+ limitby=(0, 1)).first()
+ if affiliation:
+ return "%s => %s" % (pr_pentity_represent(affiliation.child),
+ pr_pentity_represent(affiliation.parent))
+ else:
+ return current.messages.NONE
+
# =============================================================================
class S3PersonModel(S3Model):
""" Persons and Groups """
View
711 modules/eden/sync.py
@@ -0,0 +1,711 @@
+# -*- coding: utf-8 -*-
+
+""" Sahana Eden Synchronization
+
+ @author: Dominic König <dominic[at]aidiq.com>
+
+ @copyright: 2009-2012 (c) Sahana Software Foundation
+ @license: MIT
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+"""
+
+__all__ = ["SyncDataModel",
+ "sync_rheader",
+ "sync_now",
+ "sync_job_reset"]
+
+from gluon import *
+from gluon.storage import Storage
+from gluon.dal import Row
+from ..s3 import *
+
+# =============================================================================
+class SyncDataModel(S3Model):
+
+ names = ["sync_config",
+ "sync_status",
+ "sync_repository",
+ "sync_task",
+ "sync_job",
+ "sync_conflict",
+ "sync_log"]
+
+ def model(self):
+
+ T = current.T
+ db = current.db
+ request = current.request
+ s3 = current.response.s3
+
+ UNKNOWN_OPT = current.messages.UNKNOWN_OPT
+ NONE = current.messages.NONE
+
+ define_table = self.define_table
+ configure = self.configure
+ add_component = self.add_component
+ set_method = self.set_method
+
+ scheduler_task_id = s3.scheduler_task_id
+ s3_datetime_represent = lambda dt: \
+ S3DateTime.datetime_represent(dt, utc=True)
+
+ # -------------------------------------------------------------------------
+ # Configuration
+ # -------------------------------------------------------------------------
+ tablename = "sync_config"
+ table = define_table(tablename,
+ Field("proxy",
+ label=T("Proxy Server URL"),
+ requires=IS_EMPTY_OR(IS_URL(mode="generic"))),
+ *s3.meta_fields())
+
+ # Field configuration
+ table.uuid.readable = True
+ table.uuid.label = "UUID"
+
+ table.uuid.comment = DIV(_class="tooltip",
+ _title="%s|%s" % (
+ T("UUID"),
+ T("Unique identifier which THIS repository identifies itself with when sending synchronization requests.")))
+ table.proxy.comment = DIV(_class="tooltip",
+ _title="%s|%s" % (
+ T("Proxy Server URL"),
+ T("URL of the default proxy server to connect to remote repositories (if required). If only some of the repositories require the use of a proxy server, you can configure this in the respective repository configurations.")))
+
+ # CRUD Strings
+ s3.crud_strings[tablename] = Storage(
+ title_display = T("Synchronization Settings"),
+ title_update = T("Edit Synchronization Settings"),
+ msg_record_modified = T("Synchronization settings updated"))
+
+ # Resource Configuration
+ configure(tablename,
+ insertable=False,
+ deletable=False,
+ update_next=URL(c="sync", f="config", args=["1", "update"]))
+
+ # -------------------------------------------------------------------------
+ # Status
+ # -------------------------------------------------------------------------
+ tablename = "sync_status"
+ table = define_table(tablename,
+ Field("running", "boolean",
+ default=False,
+ readable=False,
+ writable=False),
+ Field("manual", "boolean",
+ default=False,
+ readable=False,
+ writable=False),
+ Field("timestmp", "datetime",
+ readable=False,
+ writable=False))
+
+ # -------------------------------------------------------------------------
+ # Repository
+ # -------------------------------------------------------------------------
+ tablename = "sync_repository"
+ table = define_table(tablename,
+ Field("name",
+ length=64,
+ notnull=True),
+ Field("url",
+ label="URL",
+ requires = IS_EMPTY_OR(
+ IS_NOT_IN_DB(db,
+ "sync_repository.url"))),
+ Field("username"),
+ Field("password", "password"),
+ Field("proxy",
+ label=T("Proxy Server URL"),
+ requires=IS_EMPTY_OR(IS_URL(mode="generic"))),
+ Field("last_status",
+ readable=False,
+ writable=False,
+ label=T("Last status")),
+ Field("accept_push", "boolean",
+ default=False,
+ label=T("Accept Push")),
+ *s3.meta_fields())
+
+ # Field configuration
+ table.uuid.label = "UUID"
+ table.uuid.readable = True
+ table.uuid.writable = True
+
+ table.name.comment = DIV(_class="tooltip",
+ _title="%s|%s" % (
+ T("Repository Name"),
+ T("Name of the repository (for you own reference)")))
+ table.url.comment = DIV(_class="tooltip",
+ _title="%s|%s" % (
+ T("Repository Base URL"),
+ T("Base URL of the remote Sahana Eden instance including application path, e.g. http://www.example.org/eden")))
+ table.proxy.comment = DIV(_class="tooltip",
+ _title="%s|%s" % (
+ T("Proxy Server URL"),
+ T("URL of the proxy server to connect to the repository (leave empty for default proxy)")))
+ table.username.comment = DIV(_class="tooltip",
+ _title="%s|%s" % (
+ T("Username"),
+ T("Username to use for authentication at the remote site.")))
+ table.password.comment = DIV(_class="tooltip",
+ _title="%s|%s" % (
+ T("Password"),
+ T("Password to use for authentication at the remote site.")))
+ table.uuid.comment = DIV(_class="tooltip",
+ _title="%s|%s" % (
+ T("Repository UUID"),
+ T("Identifier which the repository identifies itself with when sending synchronization requests.")))
+ table.accept_push.comment = DIV(_class="tooltip",
+ _title="%s|%s" % (
+ T("Accept Push"),
+ T("Accept unsolicited data transmissions from the repository.")))
+
+ # CRUD Strings
+ ADD_REPOSITORY = T("Add Repository")
+ s3.crud_strings[tablename] = Storage(
+ title_create = ADD_REPOSITORY,
+ title_display = T("Repository Configuration"),
+ title_list = T("Repositories"),
+ title_update = T("Edit Repository Configuration"),
+ title_search = T("Search for Repository"),
+ subtitle_create = T("Add Repository"),
+ subtitle_list = T("Currently Configured Repositories"),
+ label_list_button = T("List Repositories"),
+ label_create_button = ADD_REPOSITORY,
+ msg_record_created = T("Repository configured"),
+ msg_record_modified = T("Repository configuration updated"),
+ msg_record_deleted = T("Repository configuration deleted"),
+ msg_list_empty = T("No repositories configured"))
+
+ # Resource Configuration
+ configure(tablename,
+ list_fields=["name",
+ "uuid",
+ "accept_push",
+ (T("Last Synchronization"), "last_sync_time")
+ ],
+ onaccept=self.sync_repository_onaccept,
+ ondelete=self.sync_repository_ondelete,
+ create_next=URL(c="sync", f="repository", args=["[id]",
+ "task"]),
+ update_next=URL(c="sync", f="repository", args=["[id]"]))
+
+ table.virtualfields.append(SyncRepositoryVirtualFields())
+ set_method(tablename, method="now", action=sync_now)
+
+ # Reusable Fields
+ repository_id = S3ReusableField("repository_id", table,
+ requires = IS_ONE_OF(db,
+ "sync_repository.id",
+ "%(name)s"),
+ represent = self.sync_repository_represent,
+ label = T("Repository"))
+
+ # Components
+ add_component("sync_task",
+ sync_repository="repository_id")
+ add_component("sync_log",
+ sync_repository="repository_id")
+ add_component("sync_conflict",
+ sync_repository="repository_id")
+ add_component(S3Task.TASK_TABLENAME,
+ sync_repository=dict(name="job",
+ joinby="repository_id",
+ link="sync_job",
+ key="scheduler_task_id",
+ actuate="replace"))
+
+ # -------------------------------------------------------------------------
+ # Task
+ # -------------------------------------------------------------------------
+ # Synchronization mode
+ sync_mode = {
+ 1: T("pull"), # pull only
+ 2: T("push"), # push only
+ 3: T("pull and push"), # pull & push
+ 4: T("none") # do not synchronize this resource
+ }
+
+ # Strategy (allowed import methods)
+ sync_strategy = S3ImportItem.METHOD
+
+ sync_strategy_represent = lambda opt: opt and \
+ ", ".join([o for o in sync_strategy.values()
+ if o in opt]) or NONE
+
+ # Update method
+ sync_update_method = {
+ 1: T("update"), # update the existing record
+ 2: T("replace"), # replace the existing record
+ }
+
+ # Update/conflict resolution policy
+ sync_policies = S3ImportItem.POLICY
+ sync_policy = {
+ sync_policies.OTHER: T("always update"),
+ sync_policies.NEWER: T("update if newer"),
+ sync_policies.MASTER: T("update if master"),
+ sync_policies.THIS: T("never update")
+ }
+
+ sync_policy_represent = lambda opt: \
+ opt and sync_policy.get(opt, UNKNOWN_OPT) or NONE
+
+ tablename = "sync_task"
+ table = define_table(tablename,
+ Field("resource_name",
+ notnull=True),
+ repository_id(),
+ Field("last_sync", "datetime",
+ readable=True,
+ writable=False,
+ update="",
+ label=T("Last synchronized on")),
+ Field("mode", "integer",
+ requires = IS_IN_SET(sync_mode,
+ zero=None),
+ default = 3,
+ label = T("Mode"),
+ represent = lambda opt: \
+ sync_mode.get(opt, NONE)),
+ Field("strategy", "list:string",
+ requires = IS_IN_SET(sync_strategy.values(),
+ multiple=True,
+ zero=None),
+ default = sync_strategy.values(),
+ label = T("Strategy"),
+ represent = sync_strategy_represent,
+ widget = CheckboxesWidgetS3.widget),
+ Field("update_method", "integer",
+ # hide while not implemented
+ readable=False,
+ writable=False,
+ requires = IS_IN_SET(sync_update_method,
+ zero=None),
+ default = 1,
+ label = T("Update Method"),
+ represent = lambda opt: \
+ sync_update_method.get(opt,
+ NONE)),
+ Field("update_policy",
+ requires = IS_IN_SET(sync_policies,
+ zero=None),
+ default = sync_policies.NEWER,
+ label = T("Update Policy"),
+ represent = sync_policy_represent),
+ Field("conflict_policy",
+ requires = IS_IN_SET(sync_policies,
+ zero=None),
+ default = sync_policies.NEWER,
+ label = T("Conflict Policy"),
+ represent = sync_policy_represent),
+ *s3.meta_fields())
+
+ # Field configuration
+ table.resource_name.comment = DIV(_class="tooltip",
+ _title="%s|%s" % (
+ T("Resource Name"),
+ T("Table name of the resource to synchronize")))
+
+ table.mode.comment = DIV(_class="tooltip",
+ _title="%s|%s" % (
+ T("Synchronization mode"),
+ T("How data shall be transferred")))
+ table.strategy.comment = DIV(_class="tooltip",
+ _title="%s|%s" % (
+ T("Strategy"),
+ T("Which methods to apply when importing data to the local repository")))
+ table.update_method.comment = DIV(_class="tooltip",
+ _title="%s|%s" % (
+ T("Update Method"),
+ T("How local records shall be updated")))
+ table.update_policy.comment = DIV(_class="tooltip",
+ _title="%s|%s" % (
+ T("Update Policy"),
+ T("Under which conditions local records shall be updated")))
+ table.conflict_policy.comment = DIV(_class="tooltip",
+ _title="%s|%s" % (
+ T("Conflict policy"),
+ T("Under which condition a local record shall be updated if it also has been modified locally since the last synchronization")))
+
+ # CRUD Strings
+ ADD_TASK = T("Add Resource")
+ s3.crud_strings[tablename] = Storage(
+ title_create = ADD_TASK,
+ title_display = T("Resource Configuration"),
+ title_list = T("Resources"),
+ title_update = T("Edit Resource Configuration"),
+ title_search = T("Search for Resource"),
+ subtitle_create = ADD_TASK,
+ subtitle_list = T("Currently Configured Resources"),
+ label_list_button = T("List Resources"),
+ label_create_button = ADD_TASK,
+ msg_record_created = T("Resource configured"),
+ msg_record_modified = T("Resource configuration updated"),
+ msg_record_deleted = T("Resource configuration deleted"),
+ msg_list_empty = T("No resources configured yet"))
+
+ # Resource Configuration
+ configure(tablename,
+ create_onvalidation=self.sync_task_onvalidation)
+
+ # -------------------------------------------------------------------------
+ # Job
+ # -------------------------------------------------------------------------
+ tablename = "sync_job"
+ table = define_table(tablename,
+ repository_id(),
+ scheduler_task_id())
+
+ # CRUD Strings
+ ADD_JOB = T("Add Job")
+ s3.crud_strings[tablename] = Storage(
+ title_create = ADD_JOB,
+ title_display = T("Synchronization Job"),
+ title_list = T("Synchronization Schedule"),
+ title_update = T("Edit Job"),
+ title_search = T("Search for Job"),
+ subtitle_create = ADD_JOB,
+ subtitle_list = T("Currently Configured Jobs"),
+ label_list_button = T("List Jobs"),
+ label_create_button = ADD_JOB,
+ msg_record_created = T("Job added"),
+ msg_record_modified = T("Job updated updated"),
+ msg_record_deleted = T("Job deleted"),
+ msg_list_empty = T("No jobs configured yet"),
+ msg_no_match = T("No jobs configured"))
+
+ # Resource Configuration
+ set_method(tablename,
+ component_name="job",
+ method="reset",
+ action=sync_job_reset)
+
+ # -------------------------------------------------------------------------
+ # Conflicts
+ # -------------------------------------------------------------------------
+ # @todo: implement table
+ tablename = "sync_conflict"
+ table = define_table(tablename,
+ repository_id(),
+ Field("dummy"))
+
+ # Field configuration?
+ # CRUD Strings?
+
+ # Resource Configuration
+ configure(tablename,
+ insertable=False,
+ editable=False)
+
+ # Reusable Fields?
+ # Components?
+
+ # -------------------------------------------------------------------------
+ # Log
+ # -------------------------------------------------------------------------
+ tablename = "sync_log"
+ table = define_table(tablename,
+ Field("timestmp", "datetime",
+ represent=s3_datetime_represent,
+ label=T("Date/Time")),
+ repository_id(),
+ Field("resource_name"),
+ # Synchronization mode: PULL/PUSH, IN/OUT
+ Field("mode"),
+ Field("action"),
+ Field("result"),
+ Field("remote", "boolean",
+ default=False,
+ label=T("Remote Error"),
+ represent=lambda opt: opt and T("yes") or ("no")),
+ Field("message", "text"),
+ *s3.meta_fields())
+
+ # CRUD Strings
+ s3.crud_strings[tablename] = Storage(
+ title_display = T("Log Entry"),
+ title_list = T("Synchronization Log"),
+ subtitle_list = T("Synchronization Log"),
+ label_list_button = T("List All Entries"),
+ msg_record_deleted = T("Log Entry Deleted"),
+ msg_list_empty = T("No entries found"),
+ msg_no_match = T("No entries found"))
+
+ # Resource Configuration
+ configure(tablename,
+ editable=False,
+ insertable=False,
+ deletable=True,
+ orderby=~table.timestmp)
+
+ # ---------------------------------------------------------------------
+ # Return global names to s3db
+ #
+ return Storage()
+
+ # -------------------------------------------------------------------------
+ def defaults(self):
+ """ Safe defaults if module is disabled """
+
+ return Storage()
+
+ # -------------------------------------------------------------------------
+ @staticmethod
+ def sync_repository_represent(rid):
+ """ Repository representation """
+
+ db = current.db
+ s3db = current.s3db
+ NONE = current.messages.NONE
+
+ rtable = s3db.sync_repository
+ repository = db(rtable.id == rid).select(rtable.name,
+ limitby=(0, 1)).first()
+ if repository:
+ return repository.name
+ else:
+ return NONE
+
+ # -------------------------------------------------------------------------
+ @staticmethod
+ def sync_repository_ondelete(row):
+ """
+ Cleanup after repository deletion
+
+ @todo: use standard delete cascade
+ """
+
+ db = current.db
+ s3db = current.s3db
+
+ # Delete all resources in this repository
+ rtable = s3db.sync_resource
+ db(rtable.repository_id == row.id).update(deleted=True)
+
+ # Delete all jobs for this repository
+ # @todo: remove scheduler_task entry as well
+ jtable = s3db.sync_job
+ db(jtable.repository_id == row.id).update(deleted=True)
+
+ # Delete all pending conflicts of this repository
+ ctable = s3db.sync_conflict
+ db(ctable.repository_id == row.id).delete()
+
+ # Delete all log entries for this repository
+ ltable = s3db.sync_log
+ db(ltable.repository_id == row.id).delete()
+
+ return
+
+ # -------------------------------------------------------------------------
+ @staticmethod
+ def sync_repository_onaccept(form):
+ """
+ Send registration request to the peer
+ """
+
+ T = current.T
+ db = current.db
+ s3db = current.s3db
+ sync = current.manager.sync
+
+ response = current.response
+
+ try:
+ repository_id = form.vars.id
+ except:
+ return
+
+ if repository_id:
+ rtable = s3db.sync_repository
+ query = rtable.id == repository_id
+ repository = db(query).select(limitby=(0, 1)).first()
+ if repository and repository.url:
+ success = sync.request_registration(repository)
+ if not success:
+ response.warning = T("Could not auto-register at the repository, please register manually.")
+ else:
+ response.confirmation = T("Successfully registered at the repository.")
+ return
+
+ # -------------------------------------------------------------------------
+ @staticmethod
+ def sync_task_onvalidation(form):
+ """
+ Task record validation
+ """
+
+ db = current.db
+ s3db = current.s3db
+ request = current.request
+
+ repository_id = form.vars.repository_id or \
+ request.post_vars.repository_id
+ resource_name = form.vars.resource_name
+
+ if repository_id and resource_name:
+ ttable = s3db.sync_task
+ query = (ttable.repository_id == repository_id) & \
+ (ttable.resource_name == resource_name) & \
+ (ttable.deleted != True)
+ row = db(query).select(ttable.id, limitby=(0, 1)).first()
+ if row:
+ form.errors.resource_name = \
+ T("This resource is already configured for this repository")
+
+# =============================================================================
+
+class SyncRepositoryVirtualFields:
+ """ Repository virtual fields """
+
+ def last_sync_time(self):
+ """ Last synchronization date/time for this repository """
+
+ T = current.T
+ db = current.db
+ s3db = current.s3db
+
+ s3_datetime_represent = lambda dt: \
+ S3DateTime.datetime_represent(dt, utc=True)
+ table = s3db.sync_task
+ query = table.repository_id == self.sync_repository.id
+ task = db(query).select(orderby=~table.last_sync,
+ limitby=(0,1)).first()
+ if task:
+ return s3_datetime_represent(task.last_sync)
+ else:
+ return T("never")
+
+# -----------------------------------------------------------------------------
+def sync_rheader(r, tabs=[]):
+ """
+ Synchronization resource headers
+ """
+
+ T = current.T
+
+ if r.representation == "html":
+
+ if r.tablename == "sync_repository":
+ repository = r.record
+ if r.component and r.component_name=="log" and not r.component_id:
+ purge_log = A(T("Remove all log entries"),
+ _href=r.url(method="delete"))
+ else:
+ purge_log = ""
+ if repository:
+ if repository.url:
+ tabs.append((T("Manual Synchronization"), "now"))
+ rheader_tabs = s3_rheader_tabs(r, tabs)
+ rheader = DIV(TABLE(
+ TR(TH("%s: " % T("Name")),
+ repository.name,
+ TH(""),
+ purge_log),
+ TR(TH("URL: "),
+ repository.url,
+ TH(""),
+ ""),
+ ), rheader_tabs)
+ return rheader
+ return None
+
+# -------------------------------------------------------------------------
+def sync_job_reset(r, **attr):
+ """
+ RESTful method to reset a job status from FAILED to QUEUED,
+ for "Reset" action button
+ """
+
+ session = current.session
+
+ if r.interactive:
+ if r.component and r.component.alias == "job":
+ job_id = r.component_id
+ if job_id:
+ S3Task.reset(job_id)
+ session.confirmation = T("Job reactivated")
+ r.component_id = None
+ redirect(r.url(method=""))
+
+# -----------------------------------------------------------------------------
+def sync_now(r, **attr):
+ """
+ Manual synchronization of a repository
+ """
+
+ T = current.T
+ manager = current.manager
+ response = current.response
+ session = current.session
+ auth = current.auth
+
+ rheader = attr.get("rheader", None)
+ if rheader:
+ rheader = rheader(r)
+
+ output = dict(title=T("Manual Synchronization"), rheader=rheader)
+ s3task = current.s3task
+
+ sync = S3Sync()
+
+ if r.interactive:
+ if r.http in ("GET", "POST"):
+ repository = r.record
+ if not repository:
+ r.error(404, manager.ERROR.BAD_RECORD)
+ form = FORM(TABLE(
+ TR(TD(T("Click 'Start' to synchronize with this repository now:"))),
+ TR(TD(INPUT(_type="submit", _value=T("Start"))))))
+ if form.accepts(r.post_vars, session):
+ task_id = s3task.async("sync_synchronize",
+ args = [repository.id],
+ vars = dict(user_id=auth.user.id,
+ manual=True))
+ if task_id is False:
+ response.error = T("Could not initiate manual synchronization.")
+ elif task_id is None:
+ response.flash = T("Manual synchronization completed.")
+ else:
+ sync.set_status(manual=True)
+ response.flash = T("Manual synchronization started in the background.")
+ else:
+ r.error(405, manager.ERROR.BAD_METHOD)
+ else:
+ r.error(501, manager.ERROR.BAD_FORMAT)
+
+ status = sync.get_status()
+ if status.running:
+ output.update(form=T("Synchronization currently active - refresh page to update status."))
+ elif not status.manual:
+ output.update(form=form)
+ else:
+ output.update(form=T("Manual synchronization scheduled - refresh page to update status."))
+
+ response.view = "update.html"
+ return output
+
+# END =========================================================================
View
41 modules/s3/s3gis.py
@@ -1779,7 +1779,7 @@ def get_marker_and_popup(layer_id=None, # Used by S3REST: S3Resource.export_tree
Used by S3REST: S3Resource.export_tree():
@param: layer_id - db.gis_layer_feature.id
@param: marker - a default marker image (what would provide this?)
-
+
Used by S3Search: search_interactive():
@param: tablename - the tablename for a resource
@param: record - the record for a resource
@@ -1897,20 +1897,23 @@ def get_popup_tooltip(self, table, record, popup_label, popup_fields):
else:
tooltip = ""
- popup_fields = popup_fields.split("/")
- fieldname = popup_fields[0]
- try:
- value = record[fieldname]
- if value:
- field = table[fieldname]
- # @ToDo: Slow query which would be good to optimise
- represent = self.get_representation(field, value)
- # Is this faster than the simpler alternative?
- #represent = resource.table[fieldname].represent(value)
- tooltip = "%s %s" % (represent, tooltip)
- except:
- # This field isn't in the table
- pass
+ if popup_fields:
+ popup_fields = popup_fields.split("/")
+ fieldname = popup_fields[0]
+ try:
+ value = record[fieldname]
+ if value:
+ field = table[fieldname]
+ # @ToDo: Slow query which would be good to optimise
+ represent = self.get_representation(field, value)
+ # Is this faster than the simpler alternative?
+ #represent = resource.table[fieldname].represent(value)
+ tooltip = "%s %s" % (represent, tooltip)
+ except:
+ # This field isn't in the table
+ pass
+ else:
+ popup_fields = []
for fieldname in popup_fields:
try:
@@ -4133,7 +4136,7 @@ def as_javascript(self):
def as_json(self):
"""
Output the Layers as JSON
-
+
@ToDo: Support layers with SubLayer.as_dict() to pass config
dynamically between server & client
"""
@@ -4296,7 +4299,7 @@ def as_javascript(self):
return output
else:
return None
-
+
# -----------------------------------------------------------------------------
class FeatureLayer(Layer):
"""
@@ -4522,7 +4525,7 @@ def as_dict(self):
add_script(SCRIPT("google && google.load('earth', '1');", _type="text/javascript"))
if debug:
# Non-debug has this included within GeoExt.js
- add_script("scripts/gis/gxp/widgets/GoogleEarthPanel.js")
+ add_script("scripts/gis/gxp/widgets/GoogleEarthPanel.js")
elif epsg:
# Earth is the only layer which can run in non-Spherical Mercator
# @ToDo: Warning?
@@ -4643,7 +4646,7 @@ def as_javascript(self):
else:
return None
-
+
# -----------------------------------------------------------------------------
class KMLLayer(Layer):
"""
View
8 modules/s3/s3report.py
@@ -395,8 +395,8 @@ def _select_field(self, list_fields, **attr):
table = self.table
lfields, join, ljoins = resource.get_lfields(list_fields)
options = [OPTION(f.label,
- _value=f.fieldname,
- _selected= value == f.fieldname and "selected" or None)
+ _value=f.selector,
+ _selected= value == f.selector and "selected" or None)
for f in lfields
if (f.field is None or f.field.name != table._id.name) and f.show]
if len(options) < 2:
@@ -600,7 +600,7 @@ def _get_fields(self, fields=None):
# lfields: rfields resolved into list fields map
lfields, join, ljoins = resource.get_lfields(rfields)
- lfields = Storage([(f.fieldname, f) for f in lfields])
+ lfields = Storage([(f.selector, f) for f in lfields])
self.lfields = lfields
return
@@ -1198,7 +1198,7 @@ def _get_label(lfields, field, tablename, key):
fields = get_config(key, list_fields)
if fields:
for f in fields:
- if isinstance(f, (tuple, list)) and f[1] == lf.fieldname:
+ if isinstance(f, (tuple, list)) and f[1] == lf.selector:
return f[0]
if lf:
return lf.label
View
10 modules/s3/s3rest.py
@@ -70,7 +70,7 @@
from s3import import S3ImportJob
from s3sync import S3Sync
-DEBUG = False
+DEBUG = True
if DEBUG:
print >> sys.stderr, "S3REST: DEBUG MODE"
def _debug(m):
@@ -3659,7 +3659,7 @@ def readable_fields(self, subset=None):
table = self.table
if self.parent and self.linked is None:
- component = self.parent.components.get(self.name, None)
+ component = self.parent.components.get(self.alias, None)
if component:
fkey = component.fkey
elif self.linked is not None:
@@ -3791,6 +3791,10 @@ def get_lfield(self, selector, join=None, left=None):
raise KeyError("%s is not a component of %s" % (tn, tablename))
else:
tn = tablename
+ if tail:
+ original = "%s$%s" % (fn, tail)
+ else:
+ original = fn
# Load the table
table = s3db[tn]
@@ -3940,7 +3944,7 @@ def get_lfield(self, selector, join=None, left=None):
distinct=field.distinct or distinct)
return field
else:
- field = Storage(selector=selector,
+ field = Storage(selector=original,
tname = tn,
fname = fn,
colname = "%s.%s" % (tn, fn),
Please sign in to comment.
Something went wrong with that request. Please try again.