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

# Copyright 2015 Luc Saffre 

# License: BSD (see file COPYING for details) 

 

""" 

Database models for `lino.modlib.plausibility`. 

 

.. autosummary:: 

 

""" 

 

from __future__ import unicode_literals, print_function 

from builtins import object 

 

from django.db import models 

 

from lino.core.gfks import gfk2lookup 

from lino.modlib.gfks.mixins import Controllable 

from lino.modlib.users.mixins import UserAuthored 

from lino.core.roles import SiteStaff 

 

from lino.api import dd, rt, _ 

 

from .choicelists import Checker, Checkers 

 

from .roles import PlausibilityUser 

 

 

class UpdateProblem(dd.Action): 

    icon_name = 'bell' 

    label = _("Check plausibility") 

    combo_group = "plausibility" 

    fix_them = False 

    sort_index = 90 

    # custom_handler = True 

    # select_rows = False 

    default_format = None 

 

    def run_from_ui(self, ar, fix=None): 

        if fix is None: 

            fix = self.fix_them 

        Problem = rt.modules.plausibility.Problem 

        # print(20150327, ar.selected_rows) 

        for obj in ar.selected_rows: 

            assert isinstance(obj, Problem) 

            chk = obj.checker 

            owner = obj.owner 

            # not tested: what happens if the following deletes 

            # another obj from selected_rows? 

            qs = Problem.objects.filter( 

                **gfk2lookup(Problem.owner, owner, checker=chk)) 

            qs.delete() 

            chk.update_problems(owner, False, fix) 

        ar.set_response(refresh_all=True) 

 

 

class FixProblem(UpdateProblem): 

    label = _("Fix plausibility problems") 

    fix_them = True 

    sort_index = 91 

 

 

class UpdateProblemsByController(dd.Action): 

    """Updates the table of plausibility problems for a given database 

    object, also removing those messages which no longer exist. This 

    action does not change anything else in the database. 

 

    This action is automatically being installed on each model for 

    which there is at least one active :class:`Checker 

    <lino.modlib.plausibility.choicelists.Checker>`. 

 

    """ 

    icon_name = 'bell' 

    label = _("Check plausibility") 

    combo_group = "plausibility" 

    fix_them = False 

 

    def __init__(self, model): 

        self.model = model 

        super(UpdateProblemsByController, self).__init__() 

 

    def run_from_ui(self, ar, fix=None): 

        if fix is None: 

            fix = self.fix_them 

        Problem = rt.modules.plausibility.Problem 

        gfk = Problem.owner 

        checkers = get_checkable_models()[self.model] 

        for obj in ar.selected_rows: 

            assert isinstance(obj, self.model) 

            qs = Problem.objects.filter(**gfk2lookup(gfk, obj)) 

            qs.delete() 

            for chk in checkers: 

                chk.update_problems(obj, False, fix) 

        ar.set_response(refresh=True) 

 

 

class FixProblemsByController(UpdateProblemsByController): 

    """Update plausibility problems, repairing those which are 

    automatically fixable. 

 

    """ 

    label = _("Fix plausibility problems") 

    fix_them = True 

 

 

class Problem(Controllable, UserAuthored): 

    """Represents a detected plausibility problem. 

 

    Database objects of this model are considered temporary data which 

    may be updated automatically without user interaction. 

 

    .. attribute:: checker 

 

       The :class:`Checker 

       <lino.modlib.plausibility.choicelists.Checker>` which reported 

       this problem. 

 

    .. attribute:: message 

 

       The message text. This is a concatenation of all messages that 

       were yeld by the :attr:`checker`. 

 

    .. attribute:: user 

 

       The :class:`user <lino.modlib.users.models.User>` reponsible 

       for fixing this problem. 

 

       This field is being filled by the :meth:`get_responsible_user 

       <lino.modlib.plausibility.choicelists.Checker.get_responsible_user>` 

       method of the :attr:`checker`. 

 

    """ 

    class Meta(object): 

        app_label = 'plausibility' 

        verbose_name = _("Plausibility problem") 

        verbose_name_plural = _("Plausibility problems") 

        ordering = ['owner_type', 'owner_id', 'checker'] 

 

    # problem_type = ProblemTypes.field() 

    checker = Checkers.field() 

    # severity = Severities.field() 

    # feedback = Feedbacks.field(blank=True) 

    message = models.CharField(_("Message"), max_length=250) 

    # fixable = models.BooleanField(_("Fixable"), default=False) 

 

    update_problem = UpdateProblem() 

    fix_problem = FixProblem() 

 

 

dd.update_field(Problem, 'user', verbose_name=_("Responsible")) 

Problem.set_widget_options('checker', width=10) 

Problem.set_widget_options('user', width=10) 

Problem.set_widget_options('message', width=50) 

 

 

class Problems(dd.Table): 

    "The base table for :class:`Problem` objects." 

    model = 'plausibility.Problem' 

    column_names = "user owner message #fixable checker *" 

    auto_fit_column_widths = True 

    editable = False 

    cell_edit = False 

    parameters = dict( 

        user=models.ForeignKey( 

            'users.User', blank=True, null=True, 

            verbose_name=_("Responsible"), 

            help_text=_("""Only problems for this responsible.""")), 

        checker=Checkers.field( 

            blank=True, help_text=_("Only problems by this checker.")), 

        ) 

    params_layout = "user checker" 

 

    # simple_parameters = ('user', 'checker') 

    detail_layout = dd.DetailLayout(""" 

    user owner checker id 

    message""", window_size=(70, 'auto')) 

 

    @classmethod 

    def get_simple_parameters(cls): 

        s = super(Problems, cls).get_simple_parameters() 

        s.add('user') 

        s.add('checker') 

        return s 

 

 

class AllProblems(Problems): 

    """Show all plausibility problems. 

 

    This table can be opened by system managers using 

    :menuselection:`Explorer --> System --> Plausibility problems`. 

 

    """ 

    required_roles = dd.required(SiteStaff) 

 

 

class ProblemsByOwner(Problems): 

    master_key = 'owner' 

    column_names = "message checker user #fixable *" 

 

 

class ProblemsByChecker(Problems): 

    """Show the plausibility problems by checker. 

 

    This was the first use case of a slave table with a master which 

    is something else than a model instance. 

 

    """ 

    master_key = 'checker' 

 

    column_names = "user owner message #fixable *" 

 

    @classmethod 

    def get_master_instance(cls, ar, model, pk): 

        return Checkers.get_by_value(pk) 

 

    @classmethod 

    def get_filter_kw(self, ar, **kw): 

        kw.update(checker=ar.master_instance) 

        return kw 

 

 

class MyProblems(Problems): 

    """Shows the plausibility problems assigned to this user. 

 

    """ 

    required_roles = dd.required(PlausibilityUser) 

    label = _("Plausibility problems assigned to me") 

 

    @classmethod 

    def param_defaults(self, ar, **kw): 

        kw = super(MyProblems, self).param_defaults(ar, **kw) 

        kw.update(user=ar.get_user()) 

        return kw 

 

    @classmethod 

    def get_welcome_messages(cls, ar, **kw): 

        sar = ar.spawn(cls) 

        count = sar.get_total_count() 

        if count > 0: 

            msg = _("There are {0} plausibility problems assigned to you.") 

            msg = msg.format(count) 

            yield ar.href_to_request(sar, msg) 

 

 

@dd.receiver(dd.pre_analyze) 

def set_plausibility_actions(sender, **kw): 

    """Installs the :class:`UpdateProblemsByController` action on every 

    model for which there is at least one Checker 

 

    """ 

    for m in list(get_checkable_models().keys()): 

        assert m is not Problem 

        m.define_action(check_plausibility=UpdateProblemsByController(m)) 

        m.define_action(fix_problems=FixProblemsByController(m)) 

 

 

def get_checkable_models(*args): 

    """Return a `dict` mapping each model which has at least one checker 

    to a list of these checkers. 

 

    """ 

    if len(args): 

        selection = [getattr(Checkers, arg) for arg in args] 

    else: 

        selection = Checkers.objects() 

    checkable_models = dict() 

    for chk in selection: 

        for m in rt.models_by_base(chk.model, toplevel_only=True): 

            lst = checkable_models.setdefault(m, []) 

            lst.append(chk) 

    return checkable_models