Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

# -*- coding: utf-8 -*- 

# Copyright 2012-2015 Luc Saffre 

# License: BSD (see file COPYING for details) 

"""Some diagnostic utilities.""" 

 

from __future__ import unicode_literals 

from past.builtins import cmp 

from builtins import str 

from past.builtins import basestring 

from builtins import object 

 

# from textwrap import fill 

from atelier import rstgen 

 

from django.conf import settings 

from django.utils.encoding import force_text 

 

from lino.core.layouts import BaseLayout 

from lino.modlib.extjs.elems import Container, Wrapper, FieldElement 

from lino.modlib.users.choicelists import UserProfiles 

from lino.core import actors 

from lino.core.utils import get_models 

from lino.core.utils import full_model_name as fmn 

from lino.api import dd 

 

 

class Analyzer(object): 

    "The class of the :data:`lino.utils.diag.analyzer` object." 

    def __init__(self): 

        self._initialized = False 

 

    def analyze(self): 

        if self._initialized: 

            return 

        self._initialized = True 

        window_actions = dict() 

        self.custom_actions = [] 

        for a in actors.actors_list: 

            for ba in a.get_actions(): 

                if ba.action.is_window_action(): 

                    wl = ba.get_window_layout() or ba.action.params_layout 

                    if wl is not None: 

                        if isinstance(wl, basestring): 

                            raise Exception("20150323 : {0}".format(ba)) 

                            # Was used to find Exception: 20150323 : 

                            # <BoundAction(plausibility.Checkers, 

                            # <ShowDetailAction detail (u'Detail')>)> 

 

                        if not wl in window_actions: 

                            # lh = wl.get_layout_handle(ui) 

                            # for e in lh.main.walk(): 

                            #     e.loosen_requirements(a) 

                            window_actions[wl] = ba 

                else:  # if ba.action.custom_handler: 

                    self.custom_actions.append(ba) 

        l = list(window_actions.values()) 

 

        def f(a, b): 

            return cmp(str(a.full_name()), str(b.full_name())) 

        self.window_actions = list(sorted(l, f)) 

        self.custom_actions = list(sorted(self.custom_actions, f)) 

 

    def show_window_fields(self): 

        self.analyze() 

        items = [] 

        for ba in analyzer.window_actions: 

            items.append( 

                "{0} : {1}".format( 

                    ba.full_name(), layout_fields(ba))) 

 

        return rstgen.ul(items) 

 

    def show_window_permissions(self): 

        self.analyze() 

        items = [] 

        for ba in analyzer.window_actions: 

            items.append( 

                "{0} : visible for {1}".format( 

                    ba.full_name(), visible_for(ba))) 

 

        return rstgen.ul(items) 

 

    def show_action_permissions(self, *classes): 

        self.analyze() 

        items = [] 

        for ba in analyzer.custom_actions + analyzer.window_actions: 

            if isinstance(ba.action, classes): 

                items.append( 

                    "{0} : visible for {1}".format( 

                        ba.full_name(), visible_for(ba))) 

 

        return rstgen.ul(items) 

 

    def show_database_structure(self): 

        """Show a bullet list of all models and their fields.""" 

        self.analyze() 

        items = [] 

        for model in get_models(): 

            names = [] 

            # for f, m in model._meta.get_fields_with_model(): 

            for f in model._meta.concrete_fields: 

                names.append(f.name) 

            items.append( 

                "{0} : {1}".format(fmn(model), ', '.join(names))) 

 

        items = sorted(items) 

        return rstgen.ul(items) 

 

    def show_fields(self, model, field_names=None, languages=None): 

        model = dd.resolve_model(model) 

        if field_names is not None: 

            field_names = dd.fields_list(model, field_names) 

        items = [] 

        for f in model._meta.fields: 

            if field_names is None or f.name in field_names: 

                name = f.name 

                ref = model.__module__ + '.' + model.__name__ + '.' + name 

                verbose_name = force_text(f.verbose_name).strip() 

                help_text = force_text(f.help_text).replace('\n', ' ') 

                txt = "**{verbose_name}** (:attr:`{name} <{ref}>`) : " \ 

                      "{help_text}".format(**locals()) 

                items.append(txt) 

        return rstgen.ul(items) 

 

    def show_db_overview(self): 

        """Return a reStructredText-formatted "database overview" report. 

        Used by test cases in tested documents. 

 

        """ 

        from lino.core.utils import (full_model_name, sorted_models_list) 

 

        models_list = sorted_models_list() 

        apps = [p.app_label for p in settings.SITE.installed_plugins] 

        s = "%d apps: %s." % (len(apps), ", ".join(apps)) 

        s += "\n%d models:\n" % len(models_list) 

        i = 0 

        headers = [ 

            #~ "No.", 

            "Name", 

            "Default table", 

            #~ "M", 

            "#fields", 

            "#rows", 

            #~ ,"first","last" 

        ] 

        rows = [] 

        for model in models_list: 

            if True:  # model._meta.managed: 

                i += 1 

                cells = [] 

                #~ cells.append(str(i)) 

                cells.append(full_model_name(model)) 

                cells.append(model.get_default_table()) 

                #~ cells.append(str(model)) 

                #~ if model._meta.managed: 

                #~ cells.append('X') 

                #~ else: 

                #~ cells.append('') 

                cells.append(str(len(model._meta.concrete_fields))) 

                qs = model.objects.all() 

                n = qs.count() 

                cells.append(str(n)) 

                #~ if n: 

                #~ cells.append(obj2str(qs[0])) 

                #~ cells.append(obj2str(qs[n-1])) 

                #~ else: 

                #~ cells.append('') 

                #~ cells.append('') 

 

                rows.append(cells) 

        s += rstgen.table(headers, rows) 

        return s 

 

    def show_foreign_keys(self): 

        """Return a list that shows how database objects are being referred to 

        by some other database object. This information is important 

        (1) before deleting objects and (2) when merging them. 

 

        For every model we see a list of "delete handlers" and a list 

        of fields from other models that point to this model using 

        that delete handler. 

 

        Delete handlers are: 

 

        - PROTECT : refuse to delete when other objects refer to this object 

        - CASCADE : delete objects refering to this object 

        - set_on_delete : make other objects point to something else (or set 

          their pointer to None) 

 

        """ 

        self.analyze() 

        tdp = dict()  # target model -> delete handler -> pointer list 

        for target in get_models(): 

            dp = tdp.setdefault(target, dict()) 

            for m, fk in target._lino_ddh.fklist: 

                k = fk.rel.on_delete 

                p = dp.setdefault(k, []) 

                p.append((m, fk)) 

 

        def fk2str(mfk): 

            return "{0}.{1}".format(fmn(mfk[0]), mfk[1].name) 

 

        items1 = [] 

        for target, dp in list(tdp.items()): 

            items2 = [] 

            for dh, pl in list(dp.items()): 

                items2.append( 

                    "{0} : {1}".format( 

                        dh.__name__, ', '.join([fk2str(mfk) for mfk in pl]))) 

            if len(items2): 

                items2 = sorted(items2) 

                items1.append("{0} :\n{1}".format( 

                    fmn(target), rstgen.ul(items2))) 

 

        items1 = sorted(items1) 

        return rstgen.ul(items1) 

 

analyzer = Analyzer() 

"""This is a docstring 

""" 

 

 

def visible_for(ba): 

    """Shows a list of user profiles for which this action is visible.""" 

    if ba is None: 

        return "N/A" 

    visible = [] 

    hidden = [] 

    for p in UserProfiles.objects(): 

        name = p.name or p.value 

        if ba.get_view_permission(p): 

            visible.append(name) 

        else: 

            hidden.append(name) 

    if len(hidden) == 0: 

        return "all" 

    if len(visible) == 0: 

        return "nobody" 

    # if len(hidden) < len(visible): 

    #     return "all except %s" % ', '.join(hidden) 

    return ' '.join(visible) 

 

 

def layout_fields(ba): 

    wl = ba.get_window_layout() or ba.action.params_layout 

    if wl is None: 

        return '' 

    lh = wl.get_layout_handle(settings.SITE.kernel.default_ui) 

    elems = [str(f.name) for f in lh._store_fields] 

    return ', '.join(elems) 

    # return fill(' '.join(elems), 50) 

 

 

def py2rst(self, doctestfmt=False): 

    """Render any Python object as reStructuredText. 

 

    Where "any" actually means a layout or a layout element. 

    :class:`lino.core.layouts.BaseLayout` 

    :mod:`lino.modlib.extjs.elems` 

 

    If the optional argument `doctestfmt` is specified as `True`, then 

    output contains less blank lines which might be invalid 

    reStructuredText but is more doctest-friendly. 

 

    """ 

    if isinstance(self, BaseLayout): 

        lh = self.get_layout_handle(settings.SITE.kernel.default_ui) 

        return py2rst(lh.main, doctestfmt) 

 

    if isinstance(self, Wrapper): 

        self = self.wrapped 

 

    if isinstance(self, FieldElement): 

        s = "**%s** (%s)" % (str(self.field.verbose_name), self.field.name) 

    elif self.label is None: 

        s = "(%s)" % self.name 

    else: 

        s = "**%s** (%s)" % (str(self.label), self.name) 

    if visible_for(self) != visible_for(self.parent): 

        s += " [visible for %s]" % visible_for(self) 

    if isinstance(self, Container): 

        use_ul = False 

        for e in self.elements: 

            if isinstance(e, Container): 

                use_ul = True 

        children = [py2rst(e, doctestfmt) for e in self.elements] 

        if len(children): 

            if use_ul: 

                s += ':\n' 

                if not doctestfmt: 

                    s += '\n' 

                s += rstgen.ul(children) 

            else: 

                s += ": " + ', '.join(children) 

 

    return s