1 """GNUmed patient EMR tree browser.
2 """
3
4
5
6 __version__ = "$Revision: 1.111 $"
7 __author__ = "cfmoro1976@yahoo.es, sjtan@swiftdsl.com.au, Karsten.Hilbert@gmx.net"
8 __license__ = "GPL"
9
10
11 import sys, types, os.path, StringIO, codecs, logging
12
13
14 import wx
15
16
17 from Gnumed.pycommon import gmI18N, gmDispatcher, gmExceptions, gmTools
18 from Gnumed.exporters import gmPatientExporter
19 from Gnumed.business import gmEMRStructItems, gmPerson, gmSOAPimporter
20 from Gnumed.wxpython import gmGuiHelpers, gmEMRStructWidgets, gmSOAPWidgets, gmAllergyWidgets, gmNarrativeWidgets, gmPatSearchWidgets
21 from Gnumed.wxGladeWidgets import wxgScrolledEMRTreePnl, wxgSplittedEMRTreeBrowserPnl
22
23 _log = logging.getLogger('gm.ui')
24 _log.info(__version__)
25
26
28 """
29 Dump the patient's EMR from GUI client
30 @param parent - The parent widget
31 @type parent - A wx.Window instance
32 """
33
34 if parent is None:
35 raise TypeError('expected wx.Window instance as parent, got <None>')
36
37 pat = gmPerson.gmCurrentPatient()
38 if not pat.connected:
39 gmDispatcher.send(signal='statustext', msg=_('Cannot export EMR. No active patient.'))
40 return False
41
42
43 wc = "%s (*.txt)|*.txt|%s (*)|*" % (_("text files"), _("all files"))
44 defdir = os.path.abspath(os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'EMR', pat['dirname'])))
45 gmTools.mkdir(defdir)
46 fname = '%s-%s_%s.txt' % (_('emr-export'), pat['lastnames'], pat['firstnames'])
47 dlg = wx.FileDialog (
48 parent = parent,
49 message = _("Save patient's EMR as..."),
50 defaultDir = defdir,
51 defaultFile = fname,
52 wildcard = wc,
53 style = wx.SAVE
54 )
55 choice = dlg.ShowModal()
56 fname = dlg.GetPath()
57 dlg.Destroy()
58 if choice != wx.ID_OK:
59 return None
60
61 _log.debug('exporting EMR to [%s]', fname)
62
63
64 output_file = codecs.open(fname, 'wb', encoding='utf8', errors='replace')
65 exporter = gmPatientExporter.cEmrExport(patient = pat)
66 exporter.set_output_file(output_file)
67 exporter.dump_constraints()
68 exporter.dump_demographic_record(True)
69 exporter.dump_clinical_record()
70 exporter.dump_med_docs()
71 output_file.close()
72
73 gmDispatcher.send('statustext', msg = _('EMR successfully exported to file: %s') % fname, beep = False)
74 return fname
75
76 -class cEMRTree(wx.TreeCtrl, gmGuiHelpers.cTreeExpansionHistoryMixin):
77 """This wx.TreeCtrl derivative displays a tree view of the medical record."""
78
79
80 - def __init__(self, parent, id, *args, **kwds):
81 """Set up our specialised tree.
82 """
83 kwds['style'] = wx.TR_HAS_BUTTONS | wx.NO_BORDER
84 wx.TreeCtrl.__init__(self, parent, id, *args, **kwds)
85
86 gmGuiHelpers.cTreeExpansionHistoryMixin.__init__(self)
87
88 try:
89 self.__narr_display = kwds['narr_display']
90 del kwds['narr_display']
91 except KeyError:
92 self.__narr_display = None
93 self.__pat = gmPerson.gmCurrentPatient()
94 self.__curr_node = None
95 self.__exporter = gmPatientExporter.cEmrExport(patient = self.__pat)
96
97 self._old_cursor_pos = None
98
99 self.__make_popup_menus()
100 self.__register_events()
101
102
103
105 if not self.__pat.connected:
106 gmDispatcher.send(signal='statustext', msg=_('Cannot load clinical narrative. No active patient.'),)
107 return False
108
109 if not self.__populate_tree():
110 return False
111
112 return True
113
115 self.__narr_display = narrative_display
116
117
118
120 """Configures enabled event signals."""
121 wx.EVT_TREE_SEL_CHANGED (self, self.GetId(), self._on_tree_item_selected)
122 wx.EVT_TREE_ITEM_RIGHT_CLICK (self, self.GetId(), self._on_tree_item_right_clicked)
123
124
125
126 wx.EVT_TREE_ITEM_GETTOOLTIP(self, -1, self._on_tree_item_gettooltip)
127
128 gmDispatcher.connect(signal = 'narrative_mod_db', receiver = self._on_narrative_mod_db)
129 gmDispatcher.connect(signal = 'episode_mod_db', receiver = self._on_episode_mod_db)
130 gmDispatcher.connect(signal = 'health_issue_mod_db', receiver = self._on_issue_mod_db)
131
133 """Updates EMR browser data."""
134
135
136
137 wx.BeginBusyCursor()
138
139 self.snapshot_expansion()
140
141
142 self.DeleteAllItems()
143 root_item = self.AddRoot(_('EMR of %s') % self.__pat['description'])
144 self.SetPyData(root_item, None)
145 self.SetItemHasChildren(root_item, True)
146
147
148 self.__exporter.get_historical_tree(self)
149 self.__curr_node = root_item
150
151 self.SelectItem(root_item)
152 self.Expand(root_item)
153 self.__update_text_for_selected_node()
154
155 self.restore_expansion()
156
157 wx.EndBusyCursor()
158 return True
159
161 """Displays information for the selected tree node."""
162
163 if self.__narr_display is None:
164 return
165
166 if self.__curr_node is None:
167 return
168
169 node_data = self.GetPyData(self.__curr_node)
170
171
172 if isinstance(node_data, (gmEMRStructItems.cHealthIssue, types.DictType)):
173
174 if node_data['pk_health_issue'] is None:
175 txt = _('Pool of unassociated episodes:\n\n "%s"') % node_data['description']
176 else:
177 txt = node_data.format(left_margin=1, patient = self.__pat)
178
179 elif isinstance(node_data, gmEMRStructItems.cEpisode):
180 txt = node_data.format(left_margin = 1, patient = self.__pat)
181
182 elif isinstance(node_data, gmEMRStructItems.cEncounter):
183 epi = self.GetPyData(self.GetItemParent(self.__curr_node))
184 txt = node_data.format(episodes = [epi['pk_episode']], with_soap = True, left_margin = 1, patient = self.__pat)
185
186 else:
187 emr = self.__pat.get_emr()
188 txt = emr.format_summary()
189
190 self.__narr_display.Clear()
191 self.__narr_display.WriteText(txt)
192
194
195
196 self.__epi_context_popup = wx.Menu(title = _('Episode Menu'))
197
198 menu_id = wx.NewId()
199 self.__epi_context_popup.AppendItem(wx.MenuItem(self.__epi_context_popup, menu_id, _('Edit details')))
200 wx.EVT_MENU(self.__epi_context_popup, menu_id, self.__edit_episode)
201
202 menu_id = wx.NewId()
203 self.__epi_context_popup.AppendItem(wx.MenuItem(self.__epi_context_popup, menu_id, _('Delete')))
204 wx.EVT_MENU(self.__epi_context_popup, menu_id, self.__delete_episode)
205
206 menu_id = wx.NewId()
207 self.__epi_context_popup.AppendItem(wx.MenuItem(self.__epi_context_popup, menu_id, _('Promote')))
208 wx.EVT_MENU(self.__epi_context_popup, menu_id, self.__promote_episode_to_issue)
209
210 menu_id = wx.NewId()
211 self.__epi_context_popup.AppendItem(wx.MenuItem(self.__epi_context_popup, menu_id, _('Move encounters')))
212 wx.EVT_MENU(self.__epi_context_popup, menu_id, self.__move_encounters)
213
214
215 self.__enc_context_popup = wx.Menu(title = _('Encounter Menu'))
216
217 menu_id = wx.NewId()
218 self.__enc_context_popup.AppendItem(wx.MenuItem(self.__enc_context_popup, menu_id, _('Move data to another episode')))
219 wx.EVT_MENU(self.__enc_context_popup, menu_id, self.__relink_encounter_data2episode)
220
221 menu_id = wx.NewId()
222 self.__enc_context_popup.AppendItem(wx.MenuItem(self.__enc_context_popup, menu_id, _('Edit details')))
223 wx.EVT_MENU(self.__enc_context_popup, menu_id, self.__edit_encounter_details)
224
225 item = self.__enc_context_popup.Append(-1, _('Edit progress notes'))
226 self.Bind(wx.EVT_MENU, self.__edit_progress_notes, item)
227
228 item = self.__enc_context_popup.Append(-1, _('Move progress notes'))
229 self.Bind(wx.EVT_MENU, self.__move_progress_notes, item)
230
231 item = self.__enc_context_popup.Append(-1, _('Export for Medistar'))
232 self.Bind(wx.EVT_MENU, self.__export_encounter_for_medistar, item)
233
234
235 self.__issue_context_popup = wx.Menu(title = _('Health Issue Menu'))
236
237 menu_id = wx.NewId()
238 self.__issue_context_popup.AppendItem(wx.MenuItem(self.__issue_context_popup, menu_id, _('Edit details')))
239 wx.EVT_MENU(self.__issue_context_popup, menu_id, self.__edit_issue)
240
241 menu_id = wx.NewId()
242 self.__issue_context_popup.AppendItem(wx.MenuItem(self.__issue_context_popup, menu_id, _('Delete')))
243 wx.EVT_MENU(self.__issue_context_popup, menu_id, self.__delete_issue)
244
245 self.__issue_context_popup.AppendSeparator()
246
247 menu_id = wx.NewId()
248 self.__issue_context_popup.AppendItem(wx.MenuItem(self.__issue_context_popup, menu_id, _('Open to encounter level')))
249 wx.EVT_MENU(self.__issue_context_popup, menu_id, self.__expand_issue_to_encounter_level)
250
251
252
253
254 self.__root_context_popup = wx.Menu(title = _('EMR Menu'))
255
256 menu_id = wx.NewId()
257 self.__root_context_popup.AppendItem(wx.MenuItem(self.__root_context_popup, menu_id, _('create health issue')))
258 wx.EVT_MENU(self.__root_context_popup, menu_id, self.__create_issue)
259
260 menu_id = wx.NewId()
261 self.__root_context_popup.AppendItem(wx.MenuItem(self.__root_context_popup, menu_id, _('manage allergies')))
262 wx.EVT_MENU(self.__root_context_popup, menu_id, self.__document_allergy)
263
264 self.__root_context_popup.AppendSeparator()
265
266
267 expand_menu = wx.Menu()
268 self.__root_context_popup.AppendMenu(wx.NewId(), _('Open EMR to ...'), expand_menu)
269
270 menu_id = wx.NewId()
271 expand_menu.AppendItem(wx.MenuItem(expand_menu, menu_id, _('... issue level')))
272 wx.EVT_MENU(expand_menu, menu_id, self.__expand_to_issue_level)
273
274 menu_id = wx.NewId()
275 expand_menu.AppendItem(wx.MenuItem(expand_menu, menu_id, _('... episode level')))
276 wx.EVT_MENU(expand_menu, menu_id, self.__expand_to_episode_level)
277
278 menu_id = wx.NewId()
279 expand_menu.AppendItem(wx.MenuItem(expand_menu, menu_id, _('... encounter level')))
280 wx.EVT_MENU(expand_menu, menu_id, self.__expand_to_encounter_level)
281
282 - def __handle_root_context(self, pos=wx.DefaultPosition):
283 self.PopupMenu(self.__root_context_popup, pos)
284
285 - def __handle_issue_context(self, pos=wx.DefaultPosition):
286
287 self.PopupMenu(self.__issue_context_popup, pos)
288
289 - def __handle_episode_context(self, pos=wx.DefaultPosition):
290 self.__epi_context_popup.SetTitle(_('Episode %s') % self.__curr_node_data['description'])
291 self.PopupMenu(self.__epi_context_popup, pos)
292
293 - def __handle_encounter_context(self, pos=wx.DefaultPosition):
294 self.PopupMenu(self.__enc_context_popup, pos)
295
296
297
306
309
313
315 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
316 parent = self,
317 id = -1,
318 caption = _('Deleting episode'),
319 button_defs = [
320 {'label': _('Yes, delete'), 'tooltip': _('Delete the episode if possible (it must be completely empty).')},
321 {'label': _('No, cancel'), 'tooltip': _('Cancel and do NOT delete the episode.')}
322 ],
323 question = _(
324 'Are you sure you want to delete this episode ?\n'
325 '\n'
326 ' "%s"\n'
327 ) % self.__curr_node_data['description']
328 )
329 result = dlg.ShowModal()
330 if result != wx.ID_YES:
331 return
332
333 try:
334 gmEMRStructItems.delete_episode(episode = self.__curr_node_data)
335 except gmExceptions.DatabaseObjectInUseError:
336 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete episode. There is still clinical data recorded for it.'))
337 return
338
339
340
342 encounter = self.GetPyData(self.__curr_node)
343 node_parent = self.GetItemParent(self.__curr_node)
344 episode = self.GetPyData(node_parent)
345
346 gmNarrativeWidgets.move_progress_notes_to_another_encounter (
347 parent = self,
348 encounters = [encounter['pk_encounter']],
349 episodes = [episode['pk_episode']]
350 )
351
353 encounter = self.GetPyData(self.__curr_node)
354 node_parent = self.GetItemParent(self.__curr_node)
355 episode = self.GetPyData(node_parent)
356
357 gmNarrativeWidgets.manage_progress_notes (
358 parent = self,
359 encounters = [encounter['pk_encounter']],
360 episodes = [episode['pk_episode']]
361 )
362
369
371
372 node_parent = self.GetItemParent(self.__curr_node)
373 owning_episode = self.GetPyData(node_parent)
374
375 episode_selector = gmNarrativeWidgets.cMoveNarrativeDlg (
376 self,
377 -1,
378 episode = owning_episode,
379 encounter = self.__curr_node_data
380 )
381
382 result = episode_selector.ShowModal()
383 episode_selector.Destroy()
384
385 if result == wx.ID_YES:
386 self.__populate_tree()
387
390
392 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
393 parent = self,
394 id = -1,
395 caption = _('Deleting health issue'),
396 button_defs = [
397 {'label': _('Yes, delete'), 'tooltip': _('Delete the health issue if possible (it must be completely empty).')},
398 {'label': _('No, cancel'), 'tooltip': _('Cancel and do NOT delete the health issue.')}
399 ],
400 question = _(
401 'Are you sure you want to delete this health issue ?\n'
402 '\n'
403 ' "%s"\n'
404 ) % self.__curr_node_data['description']
405 )
406 result = dlg.ShowModal()
407 if result != wx.ID_YES:
408 dlg.Destroy()
409 return
410
411 dlg.Destroy()
412
413 try:
414 gmEMRStructItems.delete_health_issue(health_issue = self.__curr_node_data)
415 except gmExceptions.DatabaseObjectInUseError:
416 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete health issue. There is still clinical data recorded for it.'))
417
419
420 if not self.__curr_node.IsOk():
421 return
422
423 self.Expand(self.__curr_node)
424
425 epi, epi_cookie = self.GetFirstChild(self.__curr_node)
426 while epi.IsOk():
427 self.Expand(epi)
428 epi, epi_cookie = self.GetNextChild(self.__curr_node, epi_cookie)
429
432
440
442
443 root_item = self.GetRootItem()
444
445 if not root_item.IsOk():
446 return
447
448 self.Expand(root_item)
449
450
451 issue, issue_cookie = self.GetFirstChild(root_item)
452 while issue.IsOk():
453 self.Collapse(issue)
454 epi, epi_cookie = self.GetFirstChild(issue)
455 while epi.IsOk():
456 self.Collapse(epi)
457 epi, epi_cookie = self.GetNextChild(issue, epi_cookie)
458 issue, issue_cookie = self.GetNextChild(root_item, issue_cookie)
459
461
462 root_item = self.GetRootItem()
463
464 if not root_item.IsOk():
465 return
466
467 self.Expand(root_item)
468
469
470 issue, issue_cookie = self.GetFirstChild(root_item)
471 while issue.IsOk():
472 self.Expand(issue)
473 epi, epi_cookie = self.GetFirstChild(issue)
474 while epi.IsOk():
475 self.Collapse(epi)
476 epi, epi_cookie = self.GetNextChild(issue, epi_cookie)
477 issue, issue_cookie = self.GetNextChild(root_item, issue_cookie)
478
480
481 root_item = self.GetRootItem()
482
483 if not root_item.IsOk():
484 return
485
486 self.Expand(root_item)
487
488
489 issue, issue_cookie = self.GetFirstChild(root_item)
490 while issue.IsOk():
491 self.Expand(issue)
492 epi, epi_cookie = self.GetFirstChild(issue)
493 while epi.IsOk():
494 self.Expand(epi)
495 epi, epi_cookie = self.GetNextChild(issue, epi_cookie)
496 issue, issue_cookie = self.GetNextChild(root_item, issue_cookie)
497
504
505
506
508 wx.CallAfter(self.__update_text_for_selected_node)
509
511 wx.CallAfter(self.__populate_tree)
512
514 wx.CallAfter(self.__populate_tree)
515
517 sel_item = event.GetItem()
518 self.__curr_node = sel_item
519 self.__update_text_for_selected_node()
520 return True
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
604
605
606
607
608
609
610
611
612
613
614
615
617 """Right button clicked: display the popup for the tree"""
618
619 node = event.GetItem()
620 self.SelectItem(node)
621 self.__curr_node_data = self.GetPyData(node)
622 self.__curr_node = node
623
624 pos = wx.DefaultPosition
625 if isinstance(self.__curr_node_data, gmEMRStructItems.cHealthIssue):
626 self.__handle_issue_context(pos=pos)
627 elif isinstance(self.__curr_node_data, gmEMRStructItems.cEpisode):
628 self.__handle_episode_context(pos=pos)
629 elif isinstance(self.__curr_node_data, gmEMRStructItems.cEncounter):
630 self.__handle_encounter_context(pos=pos)
631 elif node == self.GetRootItem():
632 self.__handle_root_context()
633 elif type(self.__curr_node_data) == type({}):
634
635 pass
636 else:
637 print "error: unknown node type, no popup menu"
638 event.Skip()
639
641 """Used in sorting items.
642
643 -1: 1 < 2
644 0: 1 = 2
645 1: 1 > 2
646 """
647
648
649 item1 = self.GetPyData(node1)
650 item2 = self.GetPyData(node2)
651
652
653 if isinstance(item1, gmEMRStructItems.cEncounter):
654 if item1['started'] == item2['started']:
655 return 0
656 if item1['started'] > item2['started']:
657 return -1
658 return 1
659
660
661 if isinstance(item1, gmEMRStructItems.cEpisode):
662 start1 = item1.get_access_range()[0]
663 start2 = item2.get_access_range()[0]
664 if start1 == start2:
665 return 0
666 if start1 < start2:
667 return -1
668 return 1
669
670
671 if isinstance(item1, gmEMRStructItems.cHealthIssue):
672
673
674 if item1['grouping'] is None:
675 if item2['grouping'] is not None:
676 return 1
677
678
679 if item1['grouping'] is not None:
680 if item2['grouping'] is None:
681 return -1
682
683
684 if (item1['grouping'] is None) and (item2['grouping'] is None):
685 if item1['description'].lower() < item2['description'].lower():
686 return -1
687 if item1['description'].lower() > item2['description'].lower():
688 return 1
689 return 0
690
691
692 if item1['grouping'] < item2['grouping']:
693 return -1
694
695 if item1['grouping'] > item2['grouping']:
696 return 1
697
698 if item1['description'].lower() < item2['description'].lower():
699 return -1
700
701 if item1['description'].lower() > item2['description'].lower():
702 return 1
703
704 return 0
705
706
707 if isinstance(item1, type({})):
708 return -1
709
710 return 0
711
729
731 """A splitter window holding an EMR tree.
732
733 The left hand side displays a scrollable EMR tree while
734 on the right details for selected items are displayed.
735
736 Expects to be put into a Notebook.
737 """
739 wxgSplittedEMRTreeBrowserPnl.wxgSplittedEMRTreeBrowserPnl.__init__(self, *args, **kwds)
740 self._pnl_emr_tree._emr_tree.set_narrative_display(narrative_display = self._TCTRL_item_details)
741 self.__register_events()
742
744 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
745 return True
746
748 if self.GetParent().GetCurrentPage() == self:
749 self.repopulate_ui()
750 return True
751
753 """Fills UI with data."""
754 self._pnl_emr_tree.repopulate_ui()
755 self._splitter_browser.SetSashPosition(self._splitter_browser.GetSizeTuple()[0]/3, True)
756 return True
757
760 wx.Panel.__init__(self, *args, **kwargs)
761
762 self.__do_layout()
763 self.__register_events()
764
766 self.__journal = wx.TextCtrl (
767 self,
768 -1,
769 _('No EMR data loaded.'),
770 style = wx.TE_MULTILINE | wx.TE_READONLY
771 )
772 self.__journal.SetFont(wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL))
773
774 szr_outer = wx.BoxSizer(wx.VERTICAL)
775 szr_outer.Add(self.__journal, 1, wx.EXPAND, 0)
776
777 self.SetAutoLayout(1)
778 self.SetSizer(szr_outer)
779 szr_outer.Fit(self)
780 szr_outer.SetSizeHints(self)
781 self.Layout()
782
784 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
785
787 """Expects to be in a Notebook."""
788 if self.GetParent().GetCurrentPage() == self:
789 self.repopulate_ui()
790 return True
791
792
793
795 txt = StringIO.StringIO()
796 exporter = gmPatientExporter.cEMRJournalExporter()
797
798
799 try:
800 exporter.export(txt)
801 self.__journal.SetValue(txt.getvalue())
802 except ValueError:
803 _log.exception('cannot get EMR journal')
804 self.__journal.SetValue (_(
805 'An error occurred while retrieving the EMR\n'
806 'in journal form for the active patient.\n\n'
807 'Please check the log file for details.'
808 ))
809 txt.close()
810 self.__journal.ShowPosition(self.__journal.GetLastPosition())
811 return True
812
813
814
815 if __name__ == '__main__':
816
817 _log.info("starting emr browser...")
818
819 try:
820
821 patient = gmPerson.ask_for_patient()
822 if patient is None:
823 print "No patient. Exiting gracefully..."
824 sys.exit(0)
825 gmPatSearchWidgets.set_active_patient(patient = patient)
826
827
828 application = wx.PyWidgetTester(size=(800,600))
829 emr_browser = cEMRBrowserPanel(application.frame, -1)
830 emr_browser.refresh_tree()
831
832 application.frame.Show(True)
833 application.MainLoop()
834
835
836 if patient is not None:
837 try:
838 patient.cleanup()
839 except:
840 print "error cleaning up patient"
841 except StandardError:
842 _log.exception("unhandled exception caught !")
843
844 raise
845
846 _log.info("closing emr browser...")
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223