Home | Trees | Indices | Help |
|
---|
|
1 """GNUmed patient EMR timeline browser. 2 3 Uses the excellent TheTimlineProject. 4 """ 5 #================================================================ 6 __author__ = "Karsten.Hilbert@gmx.net" 7 __license__ = "GPL v2 or later" 8 9 # std lib 10 import sys 11 import logging 12 import os.path 13 14 15 # 3rd party 16 import wx 17 import lxml.etree as lxml_etree 18 19 20 # GNUmed libs 21 if __name__ == '__main__': 22 sys.path.insert(0, '../../') 23 from Gnumed.pycommon import gmDispatcher 24 from Gnumed.pycommon import gmTools 25 from Gnumed.pycommon import gmMimeLib 26 from Gnumed.pycommon import gmDateTime 27 28 from Gnumed.business import gmPerson 29 30 from Gnumed.wxpython import gmRegetMixin 31 32 from Gnumed.exporters import gmTimelineExporter 33 34 35 _log = logging.getLogger('gm.ui.tl') 36 37 #============================================================ 38 from Gnumed.timelinelib.canvas.data import TimePeriod 39 40 # activate experimental container features 41 from Gnumed.timelinelib.features.experimental import experimentalfeatures 42 experimentalfeatures.EXTENDED_CONTAINER_HEIGHT.set_active(True) 43 experimentalfeatures.EXTENDED_CONTAINER_STRATEGY.set_active(True) 44 45 from Gnumed.timelinelib.canvas.data.timeperiod import TimePeriod 46 from Gnumed.timelinelib.calendar.gregorian.gregorian import GregorianDateTime 47 48 #------------------------------------------------------------ 49 from Gnumed.timelinelib.canvas import TimelineCanvas # works because of __init__.py 5052266 267 #============================================================ 268 from Gnumed.wxGladeWidgets import wxgEMRTimelinePluginPnl 26954 TimelineCanvas.__init__(self, args[0]) # args[0] should be "parent" 55 56 self.__init_ui() 57 self.__register_interests()58 59 #--------------------------------------------------------61 appearance = self.GetAppearance() 62 appearance.set_balloons_visible(True) 63 appearance.set_hide_events_done(False) 64 appearance.set_colorize_weekends(True) 65 appearance.set_display_checkmark_on_events_done(True) 66 67 self.InitDragScroll(direction = wx.BOTH) 68 self.InitZoomSelect() 69 return 70 """ 71 appearance.set_legend_visible(self.config.show_legend) 72 appearance.set_minor_strip_divider_line_colour(self.config.minor_strip_divider_line_colour) 73 appearance.set_major_strip_divider_line_colour(self.config.major_strip_divider_line_colour) 74 appearance.set_now_line_colour(self.config.now_line_colour) 75 appearance.set_weekend_colour(self.config.weekend_colour) 76 appearance.set_bg_colour(self.config.bg_colour) 77 appearance.set_draw_period_events_to_right(self.config.draw_point_events_to_right) 78 appearance.set_text_below_icon(self.config.text_below_icon) 79 appearance.set_minor_strip_font(self.config.minor_strip_font) 80 appearance.set_major_strip_font(self.config.major_strip_font) 81 appearance.set_balloon_font(self.config.balloon_font) 82 appearance.set_legend_font(self.config.legend_font) 83 appearance.set_center_event_texts(self.config.center_event_texts) 84 appearance.set_never_show_period_events_as_point_events(self.config.never_show_period_events_as_point_events) 85 appearance.set_week_start(self.config.get_week_start()) 86 appearance.set_use_inertial_scrolling(self.config.use_inertial_scrolling) 87 appearance.set_fuzzy_icon(self.config.fuzzy_icon) 88 appearance.set_locked_icon(self.config.locked_icon) 89 appearance.set_hyperlink_icon(self.config.hyperlink_icon) 90 appearance.set_vertical_space_between_events(self.config.vertical_space_between_events) 91 appearance.set_skip_s_in_decade_text(self.config.skip_s_in_decade_text) 92 appearance.set_never_use_time(self.config.never_use_time) 93 appearance.set_legend_pos(self.config.legend_pos) 94 """95 #-------------------------------------------------------- 96 # event handling 97 #--------------------------------------------------------99 self.Bind(wx.EVT_MOUSEWHEEL, self._on_mousewheel_action) 100 self.Bind(wx.EVT_MOTION, self._on_mouse_motion) 101 self.Bind(wx.EVT_LEFT_DCLICK, self._on_left_dclick) 102 self.Bind(wx.EVT_LEFT_DOWN, self._on_left_down) 103 self.Bind(wx.EVT_LEFT_UP, self._on_left_up) 104 self.Bind(wx.EVT_RIGHT_DOWN, self._on_right_down) 105 self.Bind(wx.EVT_RIGHT_UP, self._on_right_up)106 107 #self.Bind(wx.EVT_MIDDLE_DOWN, self._on_middle_down) 108 109 #--------------------------------------------------------111 # not scrolling or zooming: 112 self.DisplayBalloons(evt) 113 114 # in case we are drag-scrolling: 115 self.DragScroll(evt) 116 117 # in case we are drag-zooming: 118 self.DragZoom(evt)119 120 #--------------------------------------------------------122 self.SpecialScrollVerticallyOnMouseWheel(evt)123 124 #--------------------------------------------------------126 self.CenterAtCursor(evt)127 128 #--------------------------------------------------------130 self.StartDragScroll(evt)131 132 #--------------------------------------------------------134 self.StopDragScroll()135 136 #--------------------------------------------------------138 self.StartZoomSelect(evt)139 140 #--------------------------------------------------------142 # right down-up sequence w/o mouse motion leads to 143 # "cannot zoom in deeper than 1 minute" 144 try: 145 self.StopDragZoom() 146 except ValueError: 147 _log.exception('drag-zoom w/o mouse motion')148 149 #-------------------------------------------------------- 150 # internal API 151 #--------------------------------------------------------153 now = gmDateTime.pydt_now_here() 154 g_now = GregorianDateTime(now.year, now.month, now.day, now.hour, now.minute, now.second).to_time() 155 self.Navigate(lambda tp: tp.center(g_now))156 157 #--------------------------------------------------------159 self.SetTimeline(None)160 161 #--------------------------------------------------------163 if not self._validate_timeline_file(tl_filename): 164 gmDispatcher.send(signal = 'statustext', msg = 'Timeline file failed to validate.') 165 from Gnumed.timelinelib.db import db_open 166 db = db_open(tl_filename) 167 db.display_in_canvas(self) 168 self.fit_care_era()169 170 #--------------------------------------------------------172 if filename is None: 173 filename = gmTools.get_unique_filename(suffix = '.svg') 174 self.SaveAsSvg(filename) 175 return filename176 177 #--------------------------------------------------------179 if filename is None: 180 filename = gmTools.get_unique_filename(suffix = '.png') 181 self.SaveAsPng(filename) 182 return filename183 184 #--------------------------------------------------------186 all_events = self._controller.get_timeline().get_all_events() 187 if len(all_events) == 0: 188 period4all_events = None 189 start = self._first_time(all_events) 190 end = self._last_time(all_events) 191 period4all_events = TimePeriod(start, end).zoom(-1) 192 193 if period4all_events is None: 194 return 195 if period4all_events.is_period(): 196 self.Navigate(lambda tp: tp.update(period4all_events.start_time, period4all_events.end_time)) 197 else: 198 self.Navigate(lambda tp: tp.center(period4all_events.mean_time()))199 200 #--------------------------------------------------------202 start_time = lambda event: event.get_start_time() 203 return start_time(min(events, key=start_time))204 205 #-------------------------------------------------------- 209 210 #--------------------------------------------------------212 all_eras = self._controller.get_timeline().get_all_eras() 213 care_era = [ e for e in all_eras if e.name == gmTimelineExporter.ERA_NAME_CARE_PERIOD ][0] 214 era_period = care_era.time_period 215 if era_period.is_period(): 216 self.Navigate(lambda tp: tp.update(era_period.start_time, era_period.end_time)) 217 else: 218 self.Navigate(lambda tp: tp.center(era_period.mean_time()))219 220 #--------------------------------------------------------222 end = gmDateTime.pydt_now_here() 223 g_end = GregorianDateTime(end.year, end.month, end.day, end.hour, end.minute, end.second).to_time() 224 g_start = GregorianDateTime(end.year - 1, end.month, end.day, end.hour, end.minute, end.second).to_time() 225 last_year = TimePeriod(g_start, g_end) 226 self.Navigate(lambda tp: tp.update(last_year.start_time, last_year.end_time))227 228 #--------------------------------------------------------230 xsd_name = 'timeline.xsd' 231 xsd_paths = [ 232 os.path.join(gmTools.gmPaths().system_app_data_dir, 'resources', 'timeline', xsd_name), 233 # maybe in dev tree 234 os.path.join(gmTools.gmPaths().local_base_dir, 'resources', 'timeline', xsd_name) 235 ] 236 xml_schema = None 237 for xsd_filename in xsd_paths: 238 _log.debug('XSD: %s', xsd_filename) 239 if not os.path.exists(xsd_filename): 240 _log.warning('not found') 241 continue 242 try: 243 xml_schema = lxml_etree.XMLSchema(file = xsd_filename) 244 break 245 except lxml_etree.XMLSchemaParseError: 246 _log.exception('cannot parse') 247 if xml_schema is None: 248 _log.error('no XSD found') 249 return False 250 251 with open(tl_filename, encoding = 'utf-8') as tl_file: 252 try: 253 xml_doc = lxml_etree.parse(tl_file) 254 except lxml_etree.XMLSyntaxError: 255 _log.exception('[%s] does not parse as XML', tl_filename) 256 return False 257 258 if xml_schema.validate(xml_doc): 259 _log.debug('[%s] seems valid', tl_filename) 260 return True 261 262 _log.warning('[%s] does not validate against [%s]', tl_filename, xsd_filename) 263 for entry in xml_schema.error_log: 264 _log.debug(entry) 265 return False270 -class cEMRTimelinePluginPnl(wxgEMRTimelinePluginPnl.wxgEMRTimelinePluginPnl, gmRegetMixin.cRegetOnPaintMixin):271 """Panel holding a number of widgets. Used as notebook page."""395 396 #============================================================ 397273 self.__tl_file = None 274 wxgEMRTimelinePluginPnl.wxgEMRTimelinePluginPnl.__init__(self, *args, **kwargs) 275 gmRegetMixin.cRegetOnPaintMixin.__init__(self) 276 # self.__init_ui() 277 self.__register_interests()278 279 #-------------------------------------------------------- 280 # event handling 281 #--------------------------------------------------------283 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)284 # gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_data_reget) 285 286 #--------------------------------------------------------288 self._PNL_timeline.clear_timeline()289 290 #-------------------------------------------------------- 293 294 #-------------------------------------------------------- 314 315 #-------------------------------------------------------- 321 322 #-------------------------------------------------------- 332 333 #-------------------------------------------------------- 336 337 #-------------------------------------------------------- 340 341 #-------------------------------------------------------- 344 345 #-------------------------------------------------------- 348 349 #-------------------------------------------------------- 352 353 #-------------------------------------------------------- 356 357 #-------------------------------------------------------- 358 # notebook plugin glue 359 #-------------------------------------------------------- 362 363 #-------------------------------------------------------- 364 # internal API 365 #-------------------------------------------------------- 366 # def __init_ui(self): 367 # pass 368 #-------------------------------------------------------- 369 # reget mixin API 370 # 371 # remember to call 372 # self._schedule_data_reget() 373 # whenever you learn of data changes from database 374 # listener threads, dispatcher signals etc. 375 #--------------------------------------------------------377 pat = gmPerson.gmCurrentPatient() 378 if not pat.connected: 379 return True 380 381 wx.BeginBusyCursor() 382 try: 383 self.__tl_file = gmTimelineExporter.create_timeline_file(patient = pat) 384 self._PNL_timeline.open_timeline(self.__tl_file) 385 except Exception: # more specifically: TimelineIOError 386 _log.exception('cannot load EMR from timeline XML') 387 self._PNL_timeline.clear_timeline() 388 self.__tl_file = gmTimelineExporter.create_fake_timeline_file(patient = pat) 389 self._PNL_timeline.open_timeline(self.__tl_file) 390 return True 391 finally: 392 wx.EndBusyCursor() 393 394 return True
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Fri Jun 26 01:55:29 2020 | http://epydoc.sourceforge.net |