registry.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. from django.core.urlresolvers import reverse
  2. from rapid.models import Application
  3. from django.conf.urls import url
  4. import inspect
  5. import logging
  6. from os import path
  7. def _split_all_path(file_name):
  8. file_name = path.splitdrive(file_name)[1]
  9. p = 'a'
  10. while p:
  11. file_name, p = path.split(file_name)
  12. yield p
  13. def _caller_urls_module():
  14. st = inspect.stack()
  15. for rec in st:
  16. file_name = rec[1]
  17. segments = list(_split_all_path(file_name))
  18. i = 0
  19. for i in xrange(0, len(segments) - 2):
  20. p = path.normcase(segments[i])
  21. p = path.splitext(p)[0]
  22. if p == "urls":
  23. return segments[i+1]
  24. return None
  25. def _model_name(model):
  26. if hasattr(model, "url_name"):
  27. return model.url_name
  28. return model._meta.verbose_name
  29. class MenuEntry:
  30. """
  31. The data that goes on a menu item.
  32. Model, permissions and url
  33. """
  34. def __init__(self, model, permission_set, name=None):
  35. self.name = name
  36. self.model = model
  37. self.permission_set = permission_set
  38. def get_url(self, instance=None):
  39. ats = [(x, getattr(instance, x)) for x in self.action.query_parameters]
  40. return reverse(self.url, kwargs=dict(ats))
  41. def __unicode__(self):
  42. return u"Menu entry: " + self.name + " -> " + unicode(self.action)
  43. def __str__(self):
  44. return str(unicode(self))
  45. class ModuleEntry:
  46. """
  47. Module data used at menu construction
  48. """
  49. def __init__(self, python_name, menu_name, base_url):
  50. self.python_name = python_name
  51. self.menu_name = menu_name
  52. self.base_url = url
  53. self.models = set()
  54. class Action:
  55. """
  56. An action to be done over a model.
  57. Default actions are "list", "view", "edit", "add", "delete", and "select",
  58. those are defined at the views module.
  59. """
  60. def __init__(self, name, url_parameters, query_parameters, view_factory,
  61. verbose_name=None, icon=None, visibility=None):
  62. self.name = name
  63. self.url_parameters = url_parameters
  64. self.query_parameters = query_parameters
  65. self.view_factory = view_factory
  66. self.verbose_name = verbose_name if verbose_name else name
  67. self.icon = icon
  68. self.visibility = self.Visibility.details if visibility is None else visibility
  69. def __unicode__(self):
  70. return u"Action: " + self.name
  71. def __str__(self):
  72. return str(unicode(self))
  73. class Visibility:
  74. hidden = 1
  75. details = 2
  76. list = 3
  77. class _Registry:
  78. """
  79. Registry of URLs, models and views present on the menu
  80. The registry must:
  81. -- for the menu creation
  82. list registered modules
  83. list registered models
  84. list menu entries by module
  85. list actions by model
  86. reverse url of action and model
  87. -- for crud generation
  88. query if model is registered
  89. list actions by model
  90. reverse url of action and model
  91. """
  92. def __init__(self):
  93. """
  94. Populates the menu registry
  95. """
  96. self._modules = {} # ModuleEntry by python_name
  97. self._models = {} # {'action name': MenuEntry} by model class
  98. for a in Application.objects.filter(enabled=True):
  99. m = ModuleEntry(a.python_name, a.name, a.url)
  100. self._modules[a.python_name] = m
  101. def register_action(self, action, entry, **kwargs):
  102. """
  103. Registers an action at this registry, so it will appear on the menu
  104. and can be reversed at the cruds.
  105. :param action: Action type
  106. :param entry: The menu entry where it will appear
  107. :param kwargs: Arguments (besides model) that'll be passed to the view_factory of the action
  108. :return: A Django URL pattern that should be added to the patterns of a urls.py module
  109. """
  110. from django.contrib.auth.models import User
  111. module_name = _caller_urls_module()
  112. model = entry.model
  113. if not module_name:
  114. raise Exception("Unidentified python module registering " + str(model))
  115. if not registry._modules.has_key(module_name):
  116. logging.error("Module " + module_name + " is not set-up for registering cruds")
  117. return None
  118. module_entry = registry._modules[module_name]
  119. module_entry.models.add(model)
  120. if not entry.name:
  121. entry.name = _model_name(model)
  122. entry.action = action
  123. entry_url = module_entry.menu_name + '_' + entry.name + '_' + action.name
  124. entry.url = entry_url
  125. model_actions = self._models.get(model, {})
  126. if model_actions.has_key(action.name):
  127. raise Exception("Action " + action.name + " already registered for model " + str(model))
  128. model_actions[action.name] = entry
  129. self._models[model] = model_actions
  130. return url(r'^/%s/%s/%s$' % (entry.name, action.name, action.url_parameters),
  131. action.view_factory(model=entry.model, **kwargs), name=entry_url)
  132. def get_url_of_action(self, model, action_name, **kwargs):
  133. acts = self._models.get(model)
  134. if acts and acts.has_key(action_name):
  135. return reverse(acts[action_name].url, kwargs=kwargs)
  136. def modules(self):
  137. return self._modules.values()
  138. def entry_names(self):
  139. return [x.menu_name for x in self._modules.values()]
  140. def is_controlled(self, model):
  141. return self._models.has_key(model)
  142. def model_entry(self, model):
  143. return self._models.get(model)
  144. def module_models(self, module):
  145. return self._modules[module].models
  146. registry = _Registry()