forked from Project-Platypus/Rhodium
-
Notifications
You must be signed in to change notification settings - Fork 1
/
excel.py
134 lines (102 loc) · 4.31 KB
/
excel.py
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
# Copyright 2015-2016 David Hadka
#
# This file is part of Rhodium, a Python module for robust decision making and
# exploratory modeling.
#
# Rhodium is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Rhodium is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Rhodium. If not, see <http://www.gnu.org/licenses/>.
from __future__ import division, print_function, absolute_import
import time
import six
import win32com.client
from win32com.universal import com_error
from .model import *
class ExcelHelper(object):
def __init__(self, filename, sheet=1, visible=False):
super(ExcelHelper, self).__init__()
self.xl = win32com.client.Dispatch("Excel.Application")
self.wb = self.xl.Workbooks.Open(filename)
# ensure auto-calculations is enabled
sheets = self.xl.Worksheets
for i in range(sheets.Count):
sheets.Item(i+1).EnableCalculation = True
# set the default sheet
self.set_sheet(sheet)
if visible:
self.show()
def set_sheet(self, sheet):
try:
self.sheet = self.wb.Sheets(sheet)
self.sheet_index = sheet
except com_error:
raise ValueError("invalid sheet " + str(sheet))
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
def show(self):
self.xl.Visible = 1
def hide(self):
self.xl.Visible = 0
def close(self):
if self.wb:
self.wb.Close(SaveChanges=0)
self.wb = None
if self.xl:
self.xl.DisplayAlerts = False
self.xl.Quit()
self.xl = None
def _dereference(self, cell):
try:
if isinstance(cell, six.string_types):
return self.sheet.Range(cell)
elif isinstance(cell, (list, tuple)) and len(cell) == 2:
return self.sheet.Cells(cell[0], cell[1])
except com_error:
pass
raise ValueError("invalid cell reference " + str(cell))
def __getitem__(self, cell):
return self._dereference(cell).Value
def __setitem__(self, cell, value):
# expects single value or 2D list/tuple
if isinstance(value, (list, tuple)):
value = [v if isinstance(v, (list, tuple)) else [v] for v in value]
self._dereference(cell).Value = value
class ExcelModel(Model):
def __init__(self, filename, **kwargs):
super(ExcelModel, self).__init__(self._evaluate)
self.excel_helper = ExcelHelper(filename, **kwargs)
def _evaluate(self, **kwargs):
result = {}
for parameter in self.parameters:
if hasattr(parameter, "cell"):
key = getattr(parameter, "cell")
else:
key = parameter.name
if hasattr(parameter, "sheet") and self.excel_helper.sheet_index != getattr(parameter, "sheet"):
self.excel_helper.set_sheet(getattr(parameter, "sheet"))
value = kwargs.get(parameter.name, parameter.default_value)
if value is not None:
self.excel_helper[key] = value
for response in self.responses:
if hasattr(response, "cell"):
key = getattr(response, "cell")
else:
key = response.name
if hasattr(response, "sheet") and self.excel_helper.sheet_index != getattr(response, "sheet"):
self.excel_helper.set_sheet(getattr(response, "sheet"))
result[response.name] = self.excel_helper[key]
return result
def close(self):
self.excel_helper.close()
super(ExcelModel, self).close()