This file is indexed.

/usr/lib/python2.7/dist-packages/ginga/rv/plugins/ChangeHistory.py is in python-ginga 2.6.1-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
#
# ChangeHistory.py -- ChangeHistory global plugin for Ginga.
#
from ginga import GingaPlugin
from ginga.gw import Widgets
from ginga.misc import Bunch
from ginga.util.iohelper import shorten_name


class ChangeHistory(GingaPlugin.GlobalPlugin):
    """Keep track of buffer change history.

    History should stay no matter what channel or image is active.
    New history can be added, but old history cannot be deleted,
    unless the image/channel itself is deleted.

    The :meth:`redo` method picks up a ``'modified'`` event and displays
    related metadata here. The metadata is obtained as follows:

    .. code-block:: python

        channel = self.fv.get_channel_info(chname)
        iminfo = channel.get_image_info(imname)
        timestamp = iminfo.time_modified
        description = iminfo.reason_modified  # Optional

    While ``'time_modified'`` is automatically added by Ginga,
    ``'reason_modified'`` is optional and has be to explicitly set
    by the calling plugin in the same method that issues the
    ``'modified'`` callback, like this:

    .. code-block:: python

        # This issues the 'modified' callback and sets the timestamp
        image.set_data(new_data, ...)

        # Manually add the description
        chname = self.fv.get_channel_name(self.fitsimage)
        channel = self.fv.get_channel_info(chname)
        iminfo = channel.get_image_info(image.get('name'))
        iminfo.reason_modified = 'Something was done to this image buffer'

    """
    def __init__(self, fv):
        # superclass defines some variables for us, like logger
        super(ChangeHistory, self).__init__(fv)

        self.columns = [('Timestamp (UTC)', 'MODIFIED'),
                        ('Description', 'DESCRIP'),
                        ]
        # For table-of-contents pane
        self.name_dict = Bunch.caselessDict()
        self.treeview = None

        prefs = self.fv.get_preferences()
        self.settings = prefs.createCategory('plugin_ChangeHistory')
        self.settings.addDefaults(always_expand=True,
                                  color_alternate_rows=True,
                                  ts_colwidth=250)
        self.settings.load(onError='silent')

        fv.add_callback('remove-image', self.remove_image_cb)
        fv.add_callback('delete-channel', self.delete_channel_cb)

        self.gui_up = False

    def build_gui(self, container):
        """This method is called when the plugin is invoked.  It builds the
        GUI used by the plugin into the widget layout passed as
        ``container``.

        This method could be called several times if the plugin is opened
        and closed.

        """
        vbox, sw, self.orientation = Widgets.get_oriented_box(container)
        vbox.set_border_width(4)
        vbox.set_spacing(2)

        # create the Treeview
        always_expand = self.settings.get('always_expand', True)
        color_alternate = self.settings.get('color_alternate_rows', True)
        treeview = Widgets.TreeView(auto_expand=always_expand,
                                    sortable=True,
                                    use_alt_row_color=color_alternate)
        self.treeview = treeview
        treeview.setup_table(self.columns, 3, 'MODIFIED')
        treeview.set_column_width(0, self.settings.get('ts_colwidth', 250))
        treeview.add_callback('selected', self.show_more)
        vbox.add_widget(treeview, stretch=1)

        fr = Widgets.Frame('Selected History')

        captions = (('Channel:', 'label', 'chname', 'llabel'),
                    ('Image:', 'label', 'imname', 'llabel'),
                    ('Timestamp:', 'label', 'modified', 'llabel'))
        w, b = Widgets.build_info(captions)
        self.w.update(b)

        b.chname.set_text('')
        b.chname.set_tooltip('Channel name')

        b.imname.set_text('')
        b.imname.set_tooltip('Image name')

        b.modified.set_text('')
        b.modified.set_tooltip('Timestamp (UTC)')

        captions = (('Description:-', 'llabel'), ('descrip', 'textarea'))
        w2, b = Widgets.build_info(captions)
        self.w.update(b)

        b.descrip.set_editable(False)
        b.descrip.set_wrap(True)
        b.descrip.set_text('')
        b.descrip.set_tooltip('Displays selected history entry')

        vbox2 = Widgets.VBox()
        vbox2.set_border_width(4)
        vbox2.add_widget(w)
        vbox2.add_widget(w2)

        fr.set_widget(vbox2, stretch=0)
        vbox.add_widget(fr, stretch=0)

        container.add_widget(vbox, stretch=1)

        btns = Widgets.HBox()
        btns.set_spacing(3)

        btn = Widgets.Button('Close')
        btn.add_callback('activated', lambda w: self.close())
        btns.add_widget(btn, stretch=0)
        btns.add_widget(Widgets.Label(''), stretch=1)

        container.add_widget(btns, stretch=0)

        self.gui_up = True

    def clear_selected_history(self):
        if not self.gui_up:
            return

        self.w.chname.set_text('')
        self.w.imname.set_text('')
        self.w.modified.set_text('')
        self.w.descrip.set_text('')

    def recreate_toc(self):
        self.logger.debug("Recreating table of contents...")
        self.treeview.set_tree(self.name_dict)

    def show_more(self, widget, res_dict):
        try:
            chname = list(res_dict.keys())[0]
            img_dict = res_dict[chname]
            imname = list(img_dict.keys())[0]
            entries = img_dict[imname]
            timestamp = list(entries.keys())[0]
            bnch = entries[timestamp]
        except Exception:  # The drop-down row is selected, nothing to show
            return

        # Display on GUI
        self.w.chname.set_text(chname)
        self.w.imname.set_text(shorten_name(imname, 25))
        self.w.modified.set_text(timestamp)
        self.w.descrip.set_text(bnch.DESCRIP)

    def redo(self, channel, image):
        """Add an entry with image modification info."""
        chname = channel.name
        imname = image.get('name', 'none')
        iminfo = channel.get_image_info(imname)
        timestamp = iminfo.time_modified

        # Image fell out of cache and lost its history
        if timestamp is None:
            self.remove_image_cb(self.fv, chname, imname, image.get('path'))
            return

        # Add info to internal log
        if chname not in self.name_dict:
            self.name_dict[chname] = {}

        fileDict = self.name_dict[chname]

        if imname not in fileDict:
            fileDict[imname] = {}

        # Z: Zulu time, GMT, UTC
        timestamp = timestamp.strftime('%Y-%m-%dZ %H:%M:%SZ')
        reason = iminfo.get('reason_modified', 'Not given')
        bnch = Bunch.Bunch(CHNAME=chname, NAME=imname, MODIFIED=timestamp,
                           DESCRIP=reason)
        entries = fileDict[imname]

        # timestamp is guaranteed to be unique?
        entries[timestamp] = bnch

        self.logger.debug("Added history for chname='{0}' imname='{1}' "
                          "timestamp='{2}'".format(chname, imname, timestamp))

        if self.gui_up:
            self.recreate_toc()

    def remove_image_cb(self, viewer, chname, name, path):
        """Delete entries related to deleted image."""
        if chname not in self.name_dict:
            return

        fileDict = self.name_dict[chname]

        if name not in fileDict:
            return

        del fileDict[name]
        self.logger.debug('{0} removed from ChangeHistory'.format(name))

        if not self.gui_up:
            return False

        self.clear_selected_history()
        self.recreate_toc()

    def delete_channel_cb(self, viewer, chinfo):
        """Called when a channel is deleted from the main interface.
        Parameter is chinfo (a bunch)."""
        chname = chinfo.name

        if chname not in self.name_dict:
            return

        del self.name_dict[chname]
        self.logger.debug('{0} removed from ChangeHistory'.format(chname))

        if not self.gui_up:
            return False

        self.clear_selected_history()
        self.recreate_toc()

    def clear(self):
        self.name_dict = Bunch.caselessDict()
        self.clear_selected_history()
        self.recreate_toc()

    def close(self):
        self.fv.stop_global_plugin(str(self))
        return True

    def stop(self):
        self.gui_up = False
        self.fv.show_status('')

    def start(self):
        self.recreate_toc()

    def __str__(self):
        return 'changehistory'


# Replace module docstring with config doc for auto insert by Sphinx.
# In the future, if we need the real docstring, we can append instead of
# overwrite.
from ginga.util.toolbox import generate_cfg_example  # noqa
__doc__ = generate_cfg_example('plugin_ChangeHistory', package='ginga')