use logger instead of print as often as possible
[myslice.git] / plugins / querytable / __init__.py
1 from unfold.plugin import Plugin
2
3 from myslice.settings import logger
4
5 class QueryTable (Plugin):
6
7     """A plugin for displaying a query as a list
8
9 More accurately, we consider a subject entity (say, a slice) 
10 that can be linked to any number of related entities (say, resources, or users)
11 The 'query' argument will correspond to the subject, while
12 'query_all' will fetch the complete list of 
13 possible candidates for the relationship.
14
15 Current implementation makes the following assumptions
16 * query will only retrieve for the related items a list of fields
17   that corresponds to the initial set of fields displayed in the table
18 * query_all on the contrary is expected to return the complete set of 
19   available attributes that may be of interest, so that using a QueryEditor
20   one can easily extend this table without having to query the backend
21 * checkboxes is a boolean flag, set to true if a rightmost column
22   with checkboxes is desired
23 * optionally pass columns as the initial set of columns
24   if None then this is taken from the query's fields
25 * init_key is the name of a column that should appear in both queries
26   and used internally in the plugin for checkboxes initialization. 
27   If not specified, metadata will be used to find out a primary key.
28   However in the case of nodes & slice for example, the default key
29   as returned by the metadata would be 'urn', but 'urn' could only 
30   be used for this purpose if it gets displayed initially, which is
31   not necessarily a good idea.
32   This is why a slice view would use 'hrn' here instead.
33 * datatables_options are passed to dataTables as-is; 
34   however please refrain from passing an 'aoColumns' 
35   as we use 'aoColumnDefs' instead.
36 """
37
38
39     def __init__ (self, query=None, query_all=None, 
40                   checkboxes=False, columns=None, 
41                   init_key=None,
42                   datatables_options={}, **settings):
43         Plugin.__init__ (self, **settings)
44         self.query          = query
45         # Until we have a proper way to access queries in Python
46         self.query_all      = query_all
47         self.query_all_uuid = query_all.query_uuid if query_all else None
48         self.checkboxes     = checkboxes
49
50         # XXX We need to have some hidden columns until we properly handle dynamic queries
51         if columns is not None:
52             _columns = columns
53             _hidden_columns = []
54         elif self.query:
55             logger.debug("self.query.fields = {}".format(self.query_all.fields))
56             # Columns displayed by default
57             if self.default_fields is not None:
58                 _columns = [field for field in self.default_fields if not field == 'urn']
59             else:
60                 _columns = [field for field in self.query.fields if not field == 'urn']
61             if query_all:
62                 # We need a list because sets are not JSON-serializable
63                 if self.default_fields is not None:
64                     logger.debug(self.query_all.fields)
65                     _hidden_columns = list(self.query_all.fields - set(self.default_fields))
66                 else:
67                     _hidden_columns = list(self.query_all.fields - self.query.fields)
68                 _hidden_columns.append('urn')
69             else:
70                 _hidden_columns = []
71         else:
72             _columns = []
73             _hidden_columns = []
74
75         logger.debug("_columns={}".format(_columns))
76         self.columns = { self.mapping.get(c, c) : c for c in _columns }
77         self.hidden_columns = { self.mapping.get(c, c) : c for c in _hidden_columns }
78         logger.debug("self.columns {}".format(self.columns))
79         logger.debug("self.hidden_columns {}".format(self.hidden_columns))
80
81         self.init_key=init_key
82         self.datatables_options=datatables_options
83         # if checkboxes were required, we tell datatables about this column's type
84         # so that sorting can take place on a selected-first basis (or -last of course)
85         # this relies on the template exposing the checkboxes 'th' with class 'checkbox'
86         if self.checkboxes:
87             # we use aoColumnDefs rather than aoColumns -- ignore user-provided aoColumns
88             if 'aoColumns' in self.datatables_options:
89                 logger.warning('WARNING: querytable uses aoColumnDefs, your aoColumns spec. is discarded')
90                 del self.datatables_options['aoColumns']
91             # set aoColumnDefs in datatables_options - might already have stuff in there
92             aoColumnDefs = self.datatables_options.setdefault ('aoColumnDefs',[])
93             # here 'checkbox' is the class that we give to the <th> dom elem
94             # dom-checkbox is a sorting type that we define in querytable.js
95             #aoColumnDefs.insert (0, {'aTargets': ['checkbox'], 'sSortDataType': 'dom-checkbox' } )
96
97     def template_file (self):
98         return "querytable.html"
99
100     def template_env (self, request):
101         env={}
102         env.update(self.__dict__)
103         env['columns']=self.columns
104         return env
105
106     def requirements (self):
107         reqs = {
108             'js_files' : [ "js/spin-presets.js", "js/spin.min.js", "js/jquery.spin.js", 
109                            "js/dataTables.js", "js/dataTables.bootstrap.js", "js/with-datatables.js",
110                            "js/manifold.js", "js/manifold-query.js", 
111                            "js/unfold-helper.js",
112                           # querytable.js needs to be loaded after dataTables.js as it extends 
113                           # dataTableExt.afnSortData
114                            "js/querytable.js", 
115                            ] ,
116             'css_files': [ #"css/dataTables.bootstrap.css",
117                            # hopefully temporary, when/if datatables supports sPaginationType=bootstrap3
118                            # for now we use full_numbers, with our own ad hoc css 
119                            #"css/dataTables.full_numbers.css",
120                            "css/querytable.css" , 
121                            ],
122             }
123         return reqs
124
125     # the list of things passed to the js plugin
126     def json_settings_list (self):
127         return ['plugin_uuid', 'domid', 
128                 'query_uuid', 'query_all_uuid', 
129                 'checkboxes', 'datatables_options', 
130                 'hidden_columns', 'init_key',]