1 | #!/bin/env python |
---|
2 | |
---|
3 | #====================================================== |
---|
4 | # Author: Patrick.Brockmann@cea.fr |
---|
5 | # |
---|
6 | # Date: 13/11/2012 |
---|
7 | # |
---|
8 | #====================================================== |
---|
9 | |
---|
10 | #====================================================== |
---|
11 | import csv |
---|
12 | import matplotlib.pyplot as plt |
---|
13 | import datetime as dt |
---|
14 | import sys, string, os, re |
---|
15 | import urllib2 |
---|
16 | import random |
---|
17 | |
---|
18 | #====================================================== |
---|
19 | usage = """###################################################### |
---|
20 | Usage: plot_runcard.py [-h] |
---|
21 | [--period] |
---|
22 | [--yscalelog] |
---|
23 | [--linewidth width] |
---|
24 | [--linecolor colorcode] |
---|
25 | [--showtitle] |
---|
26 | [--pattern 'pattern, options ; ...'] |
---|
27 | list |
---|
28 | |
---|
29 | |
---|
30 | Argument: |
---|
31 | Argument list is text file with one run.card file accessible by HTTP (http://...) or locally (file:///...) per line. |
---|
32 | For each line, you can add specific options (linecolor, linewidth, title, showtitle) separated by ',' (whatever order). |
---|
33 | For example : |
---|
34 | http://dods.extra.cea.fr/work/p86caub/IPSLCM5A/PROD/rcp85GHG/v3.rcp85GHG1/MONITORING/run.card , linecolor="AFBBAA", title="My text", showtitle=1 |
---|
35 | |
---|
36 | Notes: |
---|
37 | Default color are randomly choosen |
---|
38 | Default title is built from filename |
---|
39 | |
---|
40 | Examples: |
---|
41 | ./plot_runcard.py --linecolor "BBBBBB" --pattern 'p86caub, linecolor="00FF00", showtitle=1 ; p25mart, linecolor="FF0000", showtitle=1, linewidth=4' list_url_work2 |
---|
42 | ./plot_runcard.py --linecolor "BBBBBB" --pattern 'p86.*, linecolor="00FF00", showtitle=1 ; p25.*, linecolor="FF0000", showtitle=1' list_url_work2 |
---|
43 | |
---|
44 | """ |
---|
45 | |
---|
46 | options = """###################################################### |
---|
47 | Options: |
---|
48 | -h, -?, --help, -help |
---|
49 | Print this manual |
---|
50 | --period |
---|
51 | Use CumulPeriod rather than PeriodDateBegin |
---|
52 | --yscalelog |
---|
53 | Set a log axis for vertical axis (only with --period option) |
---|
54 | --linecolor FFFFFF |
---|
55 | color code in hexa |
---|
56 | --linewidth width |
---|
57 | width = float value in points |
---|
58 | --showtitle |
---|
59 | title will be displayed |
---|
60 | By default, titles are displayed only when mouse is passed over simulations |
---|
61 | --pattern 'pattern, options ; ...' |
---|
62 | ex: 'p86caub, linecolor="00FF00", showtitle=1 ; p86mart, linecolor="FF0000"' |
---|
63 | Use single quote to delimit the option and ; between patterns. |
---|
64 | Options are considered following the order : options from command, options from pattern option, options from list file. |
---|
65 | """ |
---|
66 | |
---|
67 | #====================================================== |
---|
68 | periodoption=0 |
---|
69 | yscalelogoption=0 |
---|
70 | linecoloroption=0 |
---|
71 | linewidthoption=0 |
---|
72 | showtitleoption=0 |
---|
73 | patternoption=0 |
---|
74 | while len(sys.argv[1:]) != 0 : |
---|
75 | if sys.argv[1] in ('-h','--help') : |
---|
76 | del(sys.argv[1]) |
---|
77 | print usage + options |
---|
78 | sys.exit(1) |
---|
79 | elif sys.argv[1] == '--period' : |
---|
80 | periodoption=1 |
---|
81 | del(sys.argv[1]) |
---|
82 | elif sys.argv[1] == '--yscalelog' : |
---|
83 | yscalelogoption=1 |
---|
84 | del(sys.argv[1]) |
---|
85 | elif sys.argv[1] == '--linecolor' : |
---|
86 | linecoloroptionvalue=sys.argv[2] |
---|
87 | linecoloroption=1 |
---|
88 | del(sys.argv[1]) |
---|
89 | del(sys.argv[1]) |
---|
90 | elif sys.argv[1] == '--linewidth' : |
---|
91 | linewidthoptionvalue=string.atof(sys.argv[2]) |
---|
92 | linewidthoption=1 |
---|
93 | del(sys.argv[1]) |
---|
94 | del(sys.argv[1]) |
---|
95 | elif sys.argv[1] == '--showtitle' : |
---|
96 | showtitleoptionvalue=True |
---|
97 | showtitleoption=1 |
---|
98 | del(sys.argv[1]) |
---|
99 | elif sys.argv[1] == '--pattern' : |
---|
100 | pattern=sys.argv[2].split(';') |
---|
101 | patternoption=1 |
---|
102 | del(sys.argv[1]) |
---|
103 | del(sys.argv[1]) |
---|
104 | elif re.match('-',sys.argv[1]) : |
---|
105 | print 'Error: option inconnu' |
---|
106 | print usage + options |
---|
107 | sys.exit(1) |
---|
108 | break |
---|
109 | else: |
---|
110 | break |
---|
111 | |
---|
112 | if len(sys.argv[1:]) != 1 : |
---|
113 | print usage + options |
---|
114 | sys.exit(1) |
---|
115 | |
---|
116 | #====================================================== |
---|
117 | fig = plt.figure() |
---|
118 | ax = fig.add_subplot(111) |
---|
119 | |
---|
120 | #====================================================== |
---|
121 | nbfile=len(sys.argv[1:])/2 |
---|
122 | |
---|
123 | #====================================================== |
---|
124 | finput=file(sys.argv[1]) |
---|
125 | |
---|
126 | simus_with_annotation=[] |
---|
127 | nbsimus=0 |
---|
128 | nbfiles=0 |
---|
129 | |
---|
130 | for line in finput : |
---|
131 | print '#===========================================' |
---|
132 | nbfiles=nbfiles+1 |
---|
133 | |
---|
134 | line=line.split(',') |
---|
135 | |
---|
136 | #--------------------------- |
---|
137 | # options from command |
---|
138 | fileruncard=line[0].strip() |
---|
139 | |
---|
140 | if not linecoloroption : |
---|
141 | linecolor="%06X" % random.randint(0, 16777215) # a random color for default |
---|
142 | else : |
---|
143 | linecolor=linecoloroptionvalue |
---|
144 | |
---|
145 | if not linewidthoption : |
---|
146 | linewidth=2.0 |
---|
147 | else : |
---|
148 | linewidth=linewidthoptionvalue |
---|
149 | |
---|
150 | if fileruncard[0:4] == 'http' : |
---|
151 | # remote file: http:// |
---|
152 | # http://dods.idris.fr/login |
---|
153 | if fileruncard[0:20] == 'http://dods.idris.fr' : |
---|
154 | title='.'.join(fileruncard.split('/')[3:-2]) |
---|
155 | else : |
---|
156 | # http://dods.extra.cea.fr/xxxx/login |
---|
157 | title='.'.join(fileruncard.split('/')[4:-2]) |
---|
158 | else : |
---|
159 | # local file: file:// |
---|
160 | title=os.path.basename(re.sub(r'_run.card',"",fileruncard)) |
---|
161 | |
---|
162 | if not showtitleoption : |
---|
163 | showtitle=0 |
---|
164 | else : |
---|
165 | showtitle=showtitleoptionvalue |
---|
166 | |
---|
167 | #--------------------------- |
---|
168 | # options from pattern option |
---|
169 | if patternoption : |
---|
170 | for pat in pattern : |
---|
171 | pat=pat.split(',') |
---|
172 | patternword=pat[0].strip() |
---|
173 | if re.search(patternword,fileruncard) : |
---|
174 | patternoptions=pat[1:] |
---|
175 | for i in range(0,len(patternoptions)) : |
---|
176 | exec patternoptions[i].strip() # set options if defined |
---|
177 | |
---|
178 | #--------------------------- |
---|
179 | # options from list file |
---|
180 | for i in range(1,len(line)) : |
---|
181 | #print i, line[i].strip() |
---|
182 | exec line[i].strip() # set options if defined |
---|
183 | |
---|
184 | #--------------------------- |
---|
185 | print 'file = ', fileruncard |
---|
186 | print 'linecolor = ', linecolor |
---|
187 | print 'linewidth = ', linewidth |
---|
188 | print 'title = ', title |
---|
189 | print 'showtitle = ', showtitle |
---|
190 | |
---|
191 | x,y1,y2 = [],[],[] |
---|
192 | lineref=['# CumulPeriod | PeriodDateBegin | PeriodDateEnd | RunDateBegin | RunDateEnd | RealCpuTime | UserCpuTime | SysCpuTime | ExeDate'] |
---|
193 | |
---|
194 | try: |
---|
195 | f=urllib2.urlopen(fileruncard) |
---|
196 | csv_reader = csv.reader(f) |
---|
197 | headerline = csv_reader.next() |
---|
198 | while headerline != lineref : # read until find lineref |
---|
199 | headerline = csv_reader.next() |
---|
200 | # read an extra line |
---|
201 | headerline = csv_reader.next() |
---|
202 | |
---|
203 | nblines=0 |
---|
204 | for line in csv_reader: |
---|
205 | a=line[0][1:] # to remove first character=# |
---|
206 | part=a.split('|') |
---|
207 | |
---|
208 | PeriodDateBegin=dt.datetime.strptime(part[1].strip(),'%Y%m%d') |
---|
209 | CumulPeriod=int(part[0].strip()) |
---|
210 | RunDateBegin=dt.datetime.strptime(part[3].strip(),'%Y-%m-%dT%H:%M:%S') |
---|
211 | RunDateEnd=dt.datetime.strptime(part[4].strip(),'%Y-%m-%dT%H:%M:%S') |
---|
212 | x.append(RunDateBegin) |
---|
213 | x.append(RunDateEnd) |
---|
214 | y1.append(PeriodDateBegin) |
---|
215 | y1.append(PeriodDateBegin) |
---|
216 | y2.append(CumulPeriod) |
---|
217 | y2.append(CumulPeriod) |
---|
218 | nblines=nblines+1 |
---|
219 | |
---|
220 | print '--> Read %d lines'%nblines |
---|
221 | if nblines < 10 : |
---|
222 | print '----> File skipped (not enough lines < 10)' |
---|
223 | continue |
---|
224 | |
---|
225 | if not periodoption : |
---|
226 | y=y1 |
---|
227 | else : |
---|
228 | y=y2 |
---|
229 | |
---|
230 | simu,=ax.plot(x,y,'-', linewidth=linewidth, color='#'+linecolor) |
---|
231 | lastx=x[-2] ; lasty=y[-2] # before last (last is None) |
---|
232 | simuname=fileruncard[:-9] # remove _run.card |
---|
233 | annotation=ax.annotate(title, xy=(lastx, lasty), xytext=(-20,20), |
---|
234 | textcoords='offset points', ha='center', va='bottom', |
---|
235 | bbox=dict(boxstyle='round,pad=0.4', fc='yellow', alpha=0.3), |
---|
236 | arrowprops=dict(arrowstyle='->', connectionstyle='arc3', |
---|
237 | color='black')) |
---|
238 | |
---|
239 | annotation.set_visible(showtitle) |
---|
240 | |
---|
241 | if not showtitle : # title will displayed when mouse over |
---|
242 | simus_with_annotation.append([simu,annotation]) |
---|
243 | |
---|
244 | nbsimus=nbsimus+1 |
---|
245 | |
---|
246 | except IOError : |
---|
247 | print "======> IOError when reading run.card" |
---|
248 | continue |
---|
249 | |
---|
250 | except : |
---|
251 | print "======> Error when reading run.card" |
---|
252 | continue |
---|
253 | |
---|
254 | finput.close() |
---|
255 | |
---|
256 | #====================================================== |
---|
257 | if nbsimus == 0 : |
---|
258 | print '#===========================================' |
---|
259 | print "======> No simulation to plot" |
---|
260 | sys.exit(1) |
---|
261 | |
---|
262 | #====================================================== |
---|
263 | # http://stackoverflow.com/questions/11537374/matplotlib-basemap-popup-box/11556140#11556140 |
---|
264 | |
---|
265 | def on_move(event): |
---|
266 | visibility_changed = False |
---|
267 | for simu, annotation in simus_with_annotation: |
---|
268 | should_be_visible = (simu.contains(event)[0] == True) |
---|
269 | |
---|
270 | if should_be_visible != annotation.get_visible(): |
---|
271 | visibility_changed = True |
---|
272 | x, y = event.xdata, event.ydata |
---|
273 | annotation.xy = x, y |
---|
274 | annotation.set_visible(should_be_visible) |
---|
275 | |
---|
276 | if visibility_changed: |
---|
277 | plt.draw() |
---|
278 | |
---|
279 | #====================================================== |
---|
280 | if not periodoption : |
---|
281 | #from matplotlib.ticker import MaxNLocator |
---|
282 | #ax.yaxis.set_major_locator(MaxNLocator(5)) |
---|
283 | from matplotlib.dates import YearLocator |
---|
284 | ax.yaxis.set_major_locator(YearLocator(50)) |
---|
285 | #from matplotlib.dates import AutoDateLocator |
---|
286 | #locator = AutoDateLocator(maxticks=5) |
---|
287 | #ax.yaxis.set_major_locator(locator) |
---|
288 | #ax.yaxis_date() |
---|
289 | |
---|
290 | else : # period option (not date) |
---|
291 | if yscalelogoption : |
---|
292 | ax.set_yscale('log') |
---|
293 | |
---|
294 | fig.autofmt_xdate() |
---|
295 | plt.grid(True) |
---|
296 | |
---|
297 | on_move_id = fig.canvas.mpl_connect('motion_notify_event', on_move) |
---|
298 | |
---|
299 | print '#===========================================' |
---|
300 | print 'Number of run.card indicated : %d' % nbfiles |
---|
301 | print 'Number of run.card processed : %d' % nbsimus |
---|
302 | print '#===========================================' |
---|
303 | |
---|
304 | plt.show() |
---|
305 | |
---|
306 | |
---|