Merge lp:~replaceafill/schooltool/schooltool.virginia_done_links into lp:schooltool/flourish
- schooltool.virginia_done_links
- Merge into flourish
Status: | Superseded |
---|---|
Proposed branch: | lp:~replaceafill/schooltool/schooltool.virginia_done_links |
Merge into: | lp:schooltool/flourish |
Diff against target: |
5702 lines (+5469/-0) (has conflicts) 42 files modified
.bzrignore (+10/-0) CHANGES.txt (+39/-0) GPL.txt (+340/-0) MANIFEST.in (+20/-0) Makefile (+163/-0) base.cfg (+37/-0) bootstrap.py (+260/-0) buildout.cfg (+13/-0) setup.py (+76/-0) src/schooltool/__init__.py (+1/-0) src/schooltool/virginia/__init__.py (+22/-0) src/schooltool/virginia/browser/__init__.py (+1/-0) src/schooltool/virginia/browser/configure.zcml (+357/-0) src/schooltool/virginia/browser/importer.py (+240/-0) src/schooltool/virginia/browser/report.py (+878/-0) src/schooltool/virginia/browser/resources/d3.v2.min.js (+4/-0) src/schooltool/virginia/browser/resources/virginia.css (+110/-0) src/schooltool/virginia/browser/resources/virginia.js (+163/-0) src/schooltool/virginia/browser/stests/__init__.py (+1/-0) src/schooltool/virginia/browser/stests/section_summary_report.txt (+867/-0) src/schooltool/virginia/browser/stests/test_selenium.py (+33/-0) src/schooltool/virginia/browser/stests/va_section_report.txt (+358/-0) src/schooltool/virginia/browser/templates/section_report_charts_column.pt (+40/-0) src/schooltool/virginia/browser/templates/section_report_details.pt (+26/-0) src/schooltool/virginia/browser/templates/section_report_score_colors.pt (+38/-0) src/schooltool/virginia/browser/templates/section_report_score_colors_column.pt (+21/-0) src/schooltool/virginia/browser/templates/section_report_script.pt (+31/-0) src/schooltool/virginia/browser/templates/va_section_report_chart.pt (+276/-0) src/schooltool/virginia/browser/templates/va_section_report_details.pt (+21/-0) src/schooltool/virginia/browser/templates/va_section_report_passing_score.pt (+28/-0) src/schooltool/virginia/browser/templates/va_section_report_tables.pt (+80/-0) src/schooltool/virginia/configure.zcml (+18/-0) src/schooltool/virginia/locales/__init__.py (+1/-0) src/schooltool/virginia/locales/en.po (+29/-0) src/schooltool/virginia/locales/schooltool.virginia.pot (+28/-0) src/schooltool/virginia/plugin.zcml (+6/-0) src/schooltool/virginia/scripts/getverso_xls.py (+708/-0) src/schooltool/virginia/stesting.py (+31/-0) src/schooltool/virginia/stesting.zcml (+10/-0) src/schooltool/virginia/translations.zcml (+13/-0) src/schooltool/virginia/virginia.py (+70/-0) version.txt.in (+1/-0) Conflict adding file .bzrignore. Moved existing file to .bzrignore.moved. Conflict adding file CHANGES.txt. Moved existing file to CHANGES.txt.moved. Conflict adding file GPL.txt. Moved existing file to GPL.txt.moved. Conflict adding file MANIFEST.in. Moved existing file to MANIFEST.in.moved. Conflict adding file Makefile. Moved existing file to Makefile.moved. Conflict adding file README.txt. Moved existing file to README.txt.moved. Conflict adding file base.cfg. Moved existing file to base.cfg.moved. Conflict adding file bootstrap.py. Moved existing file to bootstrap.py.moved. Conflict adding file buildout.cfg. Moved existing file to buildout.cfg.moved. Conflict adding file setup.py. Moved existing file to setup.py.moved. Conflict adding file src. Moved existing file to src.moved. Conflict adding file version.txt.in. Moved existing file to version.txt.in.moved. |
To merge this branch: | bzr merge lp:~replaceafill/schooltool/schooltool.virginia_done_links |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gediminas Paulauskas | Pending | ||
Review via email: mp+128606@code.launchpad.net |
This proposal has been superseded by a proposal from 2012-10-08.
Commit message
Description of the change
Unmerged revisions
- 87. By Douglas Cerna
-
Changed Done links styles
- 86. By Gediminas Paulauskas
-
Back to development: 0.2.3
- 85. By Gediminas Paulauskas
-
Preparing release 0.2.2
- 84. By Douglas Cerna
-
Fixed Section Summary report for students with no evaluations
- 83. By Douglas Cerna
-
Changed VA Section Report view to use a table formatter
Fixed calculation bug in attainment tables of the VA Section Report
Fixed calculation bug in scores below minimum passing in Section Summary report
Fixed breadcrumbs/active viewlets for the reports
Added header classes to the report table formatters
Moved report filters logic to base view class
Added stest infrastructure and stests for the reports
Removed unused browser extesion - 82. By Gediminas Paulauskas
-
Fix make extract-
translations - 81. By Gediminas Paulauskas
-
Back to development: 0.2.2
- 80. By Gediminas Paulauskas
-
Preparing release 0.2.1
- 79. By Douglas Cerna
-
Fixed column header in VA Section report
- 78. By Douglas Cerna
-
Added Evaluated option to skill Type filter in Section Summary view
Preview Diff
1 | === added file '.bzrignore' |
2 | --- .bzrignore 1970-01-01 00:00:00 +0000 |
3 | +++ .bzrignore 2012-10-08 23:48:26 +0000 |
4 | @@ -0,0 +1,10 @@ |
5 | +./.installed.cfg |
6 | +./bin/ |
7 | +./develop-eggs/ |
8 | +./instance/ |
9 | +./parts/ |
10 | +./python/ |
11 | +./src/schooltool.virginia.egg-info |
12 | +./src/schooltool/virginia/locales/?? |
13 | +./src/schooltool/virginia/locales/??_?? |
14 | +./dist |
15 | |
16 | === renamed file '.bzrignore' => '.bzrignore.moved' |
17 | === added file 'CHANGES.txt' |
18 | --- CHANGES.txt 1970-01-01 00:00:00 +0000 |
19 | +++ CHANGES.txt 2012-10-08 23:48:26 +0000 |
20 | @@ -0,0 +1,39 @@ |
21 | +======= |
22 | +CHANGES |
23 | +======= |
24 | + |
25 | +0.2.3 (unreleased) |
26 | +------------------ |
27 | + |
28 | +- Nothing changed yet. |
29 | + |
30 | + |
31 | +0.2.2 (2012-09-27) |
32 | +------------------ |
33 | + |
34 | +- Fixed calculation bug in attainment tables of the VA Section Report |
35 | +- Fixed calculation bug in scores below minimum passing in Section Summary report |
36 | +- Fixed breadcrumbs/tabs for the reports |
37 | +- Fixed Section Summary report for students with no evaluations |
38 | +- Added selenium infrastructure and tests for the reports |
39 | + |
40 | + |
41 | +0.2.1 (2012-09-13) |
42 | +------------------ |
43 | + |
44 | +- Fixed headers in VA Section Report |
45 | +- Added Evaluated option to skill Type filter in Section Summary view |
46 | + |
47 | + |
48 | +0.2 (2012-08-15) |
49 | +---------------- |
50 | + |
51 | +- Added initial Section Summary chart report |
52 | +- Virginia sections importer needs to allow partial import |
53 | + (https://launchpad.net/bugs/1035036) |
54 | + |
55 | + |
56 | +0.1 (2012-08-08) |
57 | +---------------- |
58 | + |
59 | +Initial release. |
60 | |
61 | === renamed file 'CHANGES.txt' => 'CHANGES.txt.moved' |
62 | === added file 'GPL.txt' |
63 | --- GPL.txt 1970-01-01 00:00:00 +0000 |
64 | +++ GPL.txt 2012-10-08 23:48:26 +0000 |
65 | @@ -0,0 +1,340 @@ |
66 | + GNU GENERAL PUBLIC LICENSE |
67 | + Version 2, June 1991 |
68 | + |
69 | + Copyright (C) 1989, 1991 Free Software Foundation, Inc. |
70 | + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
71 | + Everyone is permitted to copy and distribute verbatim copies |
72 | + of this license document, but changing it is not allowed. |
73 | + |
74 | + Preamble |
75 | + |
76 | + The licenses for most software are designed to take away your |
77 | +freedom to share and change it. By contrast, the GNU General Public |
78 | +License is intended to guarantee your freedom to share and change free |
79 | +software--to make sure the software is free for all its users. This |
80 | +General Public License applies to most of the Free Software |
81 | +Foundation's software and to any other program whose authors commit to |
82 | +using it. (Some other Free Software Foundation software is covered by |
83 | +the GNU Library General Public License instead.) You can apply it to |
84 | +your programs, too. |
85 | + |
86 | + When we speak of free software, we are referring to freedom, not |
87 | +price. Our General Public Licenses are designed to make sure that you |
88 | +have the freedom to distribute copies of free software (and charge for |
89 | +this service if you wish), that you receive source code or can get it |
90 | +if you want it, that you can change the software or use pieces of it |
91 | +in new free programs; and that you know you can do these things. |
92 | + |
93 | + To protect your rights, we need to make restrictions that forbid |
94 | +anyone to deny you these rights or to ask you to surrender the rights. |
95 | +These restrictions translate to certain responsibilities for you if you |
96 | +distribute copies of the software, or if you modify it. |
97 | + |
98 | + For example, if you distribute copies of such a program, whether |
99 | +gratis or for a fee, you must give the recipients all the rights that |
100 | +you have. You must make sure that they, too, receive or can get the |
101 | +source code. And you must show them these terms so they know their |
102 | +rights. |
103 | + |
104 | + We protect your rights with two steps: (1) copyright the software, and |
105 | +(2) offer you this license which gives you legal permission to copy, |
106 | +distribute and/or modify the software. |
107 | + |
108 | + Also, for each author's protection and ours, we want to make certain |
109 | +that everyone understands that there is no warranty for this free |
110 | +software. If the software is modified by someone else and passed on, we |
111 | +want its recipients to know that what they have is not the original, so |
112 | +that any problems introduced by others will not reflect on the original |
113 | +authors' reputations. |
114 | + |
115 | + Finally, any free program is threatened constantly by software |
116 | +patents. We wish to avoid the danger that redistributors of a free |
117 | +program will individually obtain patent licenses, in effect making the |
118 | +program proprietary. To prevent this, we have made it clear that any |
119 | +patent must be licensed for everyone's free use or not licensed at all. |
120 | + |
121 | + The precise terms and conditions for copying, distribution and |
122 | +modification follow. |
123 | + |
124 | |
125 | + GNU GENERAL PUBLIC LICENSE |
126 | + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION |
127 | + |
128 | + 0. This License applies to any program or other work which contains |
129 | +a notice placed by the copyright holder saying it may be distributed |
130 | +under the terms of this General Public License. The "Program", below, |
131 | +refers to any such program or work, and a "work based on the Program" |
132 | +means either the Program or any derivative work under copyright law: |
133 | +that is to say, a work containing the Program or a portion of it, |
134 | +either verbatim or with modifications and/or translated into another |
135 | +language. (Hereinafter, translation is included without limitation in |
136 | +the term "modification".) Each licensee is addressed as "you". |
137 | + |
138 | +Activities other than copying, distribution and modification are not |
139 | +covered by this License; they are outside its scope. The act of |
140 | +running the Program is not restricted, and the output from the Program |
141 | +is covered only if its contents constitute a work based on the |
142 | +Program (independent of having been made by running the Program). |
143 | +Whether that is true depends on what the Program does. |
144 | + |
145 | + 1. You may copy and distribute verbatim copies of the Program's |
146 | +source code as you receive it, in any medium, provided that you |
147 | +conspicuously and appropriately publish on each copy an appropriate |
148 | +copyright notice and disclaimer of warranty; keep intact all the |
149 | +notices that refer to this License and to the absence of any warranty; |
150 | +and give any other recipients of the Program a copy of this License |
151 | +along with the Program. |
152 | + |
153 | +You may charge a fee for the physical act of transferring a copy, and |
154 | +you may at your option offer warranty protection in exchange for a fee. |
155 | + |
156 | + 2. You may modify your copy or copies of the Program or any portion |
157 | +of it, thus forming a work based on the Program, and copy and |
158 | +distribute such modifications or work under the terms of Section 1 |
159 | +above, provided that you also meet all of these conditions: |
160 | + |
161 | + a) You must cause the modified files to carry prominent notices |
162 | + stating that you changed the files and the date of any change. |
163 | + |
164 | + b) You must cause any work that you distribute or publish, that in |
165 | + whole or in part contains or is derived from the Program or any |
166 | + part thereof, to be licensed as a whole at no charge to all third |
167 | + parties under the terms of this License. |
168 | + |
169 | + c) If the modified program normally reads commands interactively |
170 | + when run, you must cause it, when started running for such |
171 | + interactive use in the most ordinary way, to print or display an |
172 | + announcement including an appropriate copyright notice and a |
173 | + notice that there is no warranty (or else, saying that you provide |
174 | + a warranty) and that users may redistribute the program under |
175 | + these conditions, and telling the user how to view a copy of this |
176 | + License. (Exception: if the Program itself is interactive but |
177 | + does not normally print such an announcement, your work based on |
178 | + the Program is not required to print an announcement.) |
179 | + |
180 | |
181 | +These requirements apply to the modified work as a whole. If |
182 | +identifiable sections of that work are not derived from the Program, |
183 | +and can be reasonably considered independent and separate works in |
184 | +themselves, then this License, and its terms, do not apply to those |
185 | +sections when you distribute them as separate works. But when you |
186 | +distribute the same sections as part of a whole which is a work based |
187 | +on the Program, the distribution of the whole must be on the terms of |
188 | +this License, whose permissions for other licensees extend to the |
189 | +entire whole, and thus to each and every part regardless of who wrote it. |
190 | + |
191 | +Thus, it is not the intent of this section to claim rights or contest |
192 | +your rights to work written entirely by you; rather, the intent is to |
193 | +exercise the right to control the distribution of derivative or |
194 | +collective works based on the Program. |
195 | + |
196 | +In addition, mere aggregation of another work not based on the Program |
197 | +with the Program (or with a work based on the Program) on a volume of |
198 | +a storage or distribution medium does not bring the other work under |
199 | +the scope of this License. |
200 | + |
201 | + 3. You may copy and distribute the Program (or a work based on it, |
202 | +under Section 2) in object code or executable form under the terms of |
203 | +Sections 1 and 2 above provided that you also do one of the following: |
204 | + |
205 | + a) Accompany it with the complete corresponding machine-readable |
206 | + source code, which must be distributed under the terms of Sections |
207 | + 1 and 2 above on a medium customarily used for software interchange; or, |
208 | + |
209 | + b) Accompany it with a written offer, valid for at least three |
210 | + years, to give any third party, for a charge no more than your |
211 | + cost of physically performing source distribution, a complete |
212 | + machine-readable copy of the corresponding source code, to be |
213 | + distributed under the terms of Sections 1 and 2 above on a medium |
214 | + customarily used for software interchange; or, |
215 | + |
216 | + c) Accompany it with the information you received as to the offer |
217 | + to distribute corresponding source code. (This alternative is |
218 | + allowed only for noncommercial distribution and only if you |
219 | + received the program in object code or executable form with such |
220 | + an offer, in accord with Subsection b above.) |
221 | + |
222 | +The source code for a work means the preferred form of the work for |
223 | +making modifications to it. For an executable work, complete source |
224 | +code means all the source code for all modules it contains, plus any |
225 | +associated interface definition files, plus the scripts used to |
226 | +control compilation and installation of the executable. However, as a |
227 | +special exception, the source code distributed need not include |
228 | +anything that is normally distributed (in either source or binary |
229 | +form) with the major components (compiler, kernel, and so on) of the |
230 | +operating system on which the executable runs, unless that component |
231 | +itself accompanies the executable. |
232 | + |
233 | +If distribution of executable or object code is made by offering |
234 | +access to copy from a designated place, then offering equivalent |
235 | +access to copy the source code from the same place counts as |
236 | +distribution of the source code, even though third parties are not |
237 | +compelled to copy the source along with the object code. |
238 | + |
239 | |
240 | + 4. You may not copy, modify, sublicense, or distribute the Program |
241 | +except as expressly provided under this License. Any attempt |
242 | +otherwise to copy, modify, sublicense or distribute the Program is |
243 | +void, and will automatically terminate your rights under this License. |
244 | +However, parties who have received copies, or rights, from you under |
245 | +this License will not have their licenses terminated so long as such |
246 | +parties remain in full compliance. |
247 | + |
248 | + 5. You are not required to accept this License, since you have not |
249 | +signed it. However, nothing else grants you permission to modify or |
250 | +distribute the Program or its derivative works. These actions are |
251 | +prohibited by law if you do not accept this License. Therefore, by |
252 | +modifying or distributing the Program (or any work based on the |
253 | +Program), you indicate your acceptance of this License to do so, and |
254 | +all its terms and conditions for copying, distributing or modifying |
255 | +the Program or works based on it. |
256 | + |
257 | + 6. Each time you redistribute the Program (or any work based on the |
258 | +Program), the recipient automatically receives a license from the |
259 | +original licensor to copy, distribute or modify the Program subject to |
260 | +these terms and conditions. You may not impose any further |
261 | +restrictions on the recipients' exercise of the rights granted herein. |
262 | +You are not responsible for enforcing compliance by third parties to |
263 | +this License. |
264 | + |
265 | + 7. If, as a consequence of a court judgment or allegation of patent |
266 | +infringement or for any other reason (not limited to patent issues), |
267 | +conditions are imposed on you (whether by court order, agreement or |
268 | +otherwise) that contradict the conditions of this License, they do not |
269 | +excuse you from the conditions of this License. If you cannot |
270 | +distribute so as to satisfy simultaneously your obligations under this |
271 | +License and any other pertinent obligations, then as a consequence you |
272 | +may not distribute the Program at all. For example, if a patent |
273 | +license would not permit royalty-free redistribution of the Program by |
274 | +all those who receive copies directly or indirectly through you, then |
275 | +the only way you could satisfy both it and this License would be to |
276 | +refrain entirely from distribution of the Program. |
277 | + |
278 | +If any portion of this section is held invalid or unenforceable under |
279 | +any particular circumstance, the balance of the section is intended to |
280 | +apply and the section as a whole is intended to apply in other |
281 | +circumstances. |
282 | + |
283 | +It is not the purpose of this section to induce you to infringe any |
284 | +patents or other property right claims or to contest validity of any |
285 | +such claims; this section has the sole purpose of protecting the |
286 | +integrity of the free software distribution system, which is |
287 | +implemented by public license practices. Many people have made |
288 | +generous contributions to the wide range of software distributed |
289 | +through that system in reliance on consistent application of that |
290 | +system; it is up to the author/donor to decide if he or she is willing |
291 | +to distribute software through any other system and a licensee cannot |
292 | +impose that choice. |
293 | + |
294 | +This section is intended to make thoroughly clear what is believed to |
295 | +be a consequence of the rest of this License. |
296 | + |
297 | |
298 | + 8. If the distribution and/or use of the Program is restricted in |
299 | +certain countries either by patents or by copyrighted interfaces, the |
300 | +original copyright holder who places the Program under this License |
301 | +may add an explicit geographical distribution limitation excluding |
302 | +those countries, so that distribution is permitted only in or among |
303 | +countries not thus excluded. In such case, this License incorporates |
304 | +the limitation as if written in the body of this License. |
305 | + |
306 | + 9. The Free Software Foundation may publish revised and/or new versions |
307 | +of the General Public License from time to time. Such new versions will |
308 | +be similar in spirit to the present version, but may differ in detail to |
309 | +address new problems or concerns. |
310 | + |
311 | +Each version is given a distinguishing version number. If the Program |
312 | +specifies a version number of this License which applies to it and "any |
313 | +later version", you have the option of following the terms and conditions |
314 | +either of that version or of any later version published by the Free |
315 | +Software Foundation. If the Program does not specify a version number of |
316 | +this License, you may choose any version ever published by the Free Software |
317 | +Foundation. |
318 | + |
319 | + 10. If you wish to incorporate parts of the Program into other free |
320 | +programs whose distribution conditions are different, write to the author |
321 | +to ask for permission. For software which is copyrighted by the Free |
322 | +Software Foundation, write to the Free Software Foundation; we sometimes |
323 | +make exceptions for this. Our decision will be guided by the two goals |
324 | +of preserving the free status of all derivatives of our free software and |
325 | +of promoting the sharing and reuse of software generally. |
326 | + |
327 | + NO WARRANTY |
328 | + |
329 | + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY |
330 | +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN |
331 | +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES |
332 | +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED |
333 | +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
334 | +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS |
335 | +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE |
336 | +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, |
337 | +REPAIR OR CORRECTION. |
338 | + |
339 | + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
340 | +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR |
341 | +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, |
342 | +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING |
343 | +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED |
344 | +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY |
345 | +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER |
346 | +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE |
347 | +POSSIBILITY OF SUCH DAMAGES. |
348 | + |
349 | + END OF TERMS AND CONDITIONS |
350 | + |
351 | |
352 | + How to Apply These Terms to Your New Programs |
353 | + |
354 | + If you develop a new program, and you want it to be of the greatest |
355 | +possible use to the public, the best way to achieve this is to make it |
356 | +free software which everyone can redistribute and change under these terms. |
357 | + |
358 | + To do so, attach the following notices to the program. It is safest |
359 | +to attach them to the start of each source file to most effectively |
360 | +convey the exclusion of warranty; and each file should have at least |
361 | +the "copyright" line and a pointer to where the full notice is found. |
362 | + |
363 | + <one line to give the program's name and a brief idea of what it does.> |
364 | + Copyright (C) <year> <name of author> |
365 | + |
366 | + This program is free software; you can redistribute it and/or modify |
367 | + it under the terms of the GNU General Public License as published by |
368 | + the Free Software Foundation; either version 2 of the License, or |
369 | + (at your option) any later version. |
370 | + |
371 | + This program is distributed in the hope that it will be useful, |
372 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
373 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
374 | + GNU General Public License for more details. |
375 | + |
376 | + You should have received a copy of the GNU General Public License |
377 | + along with this program; if not, write to the Free Software |
378 | + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
379 | + |
380 | + |
381 | +Also add information on how to contact you by electronic and paper mail. |
382 | + |
383 | +If the program is interactive, make it output a short notice like this |
384 | +when it starts in an interactive mode: |
385 | + |
386 | + Gnomovision version 69, Copyright (C) year name of author |
387 | + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. |
388 | + This is free software, and you are welcome to redistribute it |
389 | + under certain conditions; type `show c' for details. |
390 | + |
391 | +The hypothetical commands `show w' and `show c' should show the appropriate |
392 | +parts of the General Public License. Of course, the commands you use may |
393 | +be called something other than `show w' and `show c'; they could even be |
394 | +mouse-clicks or menu items--whatever suits your program. |
395 | + |
396 | +You should also get your employer (if you work as a programmer) or your |
397 | +school, if any, to sign a "copyright disclaimer" for the program, if |
398 | +necessary. Here is a sample; alter the names: |
399 | + |
400 | + Yoyodyne, Inc., hereby disclaims all copyright interest in the program |
401 | + `Gnomovision' (which makes passes at compilers) written by James Hacker. |
402 | + |
403 | + <signature of Ty Coon>, 1 April 1989 |
404 | + Ty Coon, President of Vice |
405 | + |
406 | +This General Public License does not permit incorporating your program into |
407 | +proprietary programs. If your program is a subroutine library, you may |
408 | +consider it more useful to permit linking proprietary applications with the |
409 | +library. If this is what you want to do, use the GNU Library General |
410 | +Public License instead of this License. |
411 | |
412 | === renamed file 'GPL.txt' => 'GPL.txt.moved' |
413 | === added file 'MANIFEST.in' |
414 | --- MANIFEST.in 1970-01-01 00:00:00 +0000 |
415 | +++ MANIFEST.in 2012-10-08 23:48:26 +0000 |
416 | @@ -0,0 +1,20 @@ |
417 | +recursive-include src *.zcml |
418 | +recursive-include src *.js |
419 | +recursive-include src *.pt |
420 | +recursive-include src *.css |
421 | +recursive-include src *.pot |
422 | +recursive-include src *.po |
423 | +recursive-include src *.mo |
424 | +recursive-include src *.txt |
425 | +recursive-include src *.png |
426 | +recursive-include src *.gif |
427 | +recursive-include src *.xml |
428 | +recursive-include src *.xls |
429 | +include version.txt |
430 | +include version.txt.in |
431 | +include CHANGES.txt |
432 | +include GPL.txt |
433 | +include Makefile |
434 | +include base.cfg |
435 | +include buildout.cfg |
436 | +include bootstrap.py |
437 | |
438 | === renamed file 'MANIFEST.in' => 'MANIFEST.in.moved' |
439 | === added file 'Makefile' |
440 | --- Makefile 1970-01-01 00:00:00 +0000 |
441 | +++ Makefile 2012-10-08 23:48:26 +0000 |
442 | @@ -0,0 +1,163 @@ |
443 | +#!/usr/bin/make |
444 | + |
445 | +PACKAGE=schooltool.virginia |
446 | +LOCALES=src/schooltool/virginia/locales |
447 | +TRANSLATIONS_ZCML=schooltool/virginia/translations.zcml |
448 | + |
449 | +DIST=/home/ftp/pub/schooltool/flourish |
450 | +PYTHON=python |
451 | + |
452 | +INSTANCE_TYPE=schooltool |
453 | +BUILDOUT_FLAGS= |
454 | + |
455 | +.PHONY: all |
456 | +all: build |
457 | + |
458 | +.PHONY: build |
459 | +build: .installed.cfg |
460 | + |
461 | +python: |
462 | + rm -rf python |
463 | + virtualenv --no-site-packages -p $(PYTHON) python |
464 | + |
465 | +.PHONY: bootstrap |
466 | +bootstrap bin/buildout: python |
467 | + python/bin/python bootstrap.py |
468 | + |
469 | +.PHONY: buildout |
470 | +buildout .installed.cfg: python bin/buildout buildout.cfg base.cfg setup.py |
471 | + bin/buildout $(BUILDOUT_FLAGS) |
472 | + |
473 | +.PHONY: update |
474 | +update: |
475 | + bzr up |
476 | + $(MAKE) buildout BUILDOUT_FLAGS=-n |
477 | + |
478 | +instance: | build |
479 | + bin/make-schooltool-instance instance instance_type=$(INSTANCE_TYPE) |
480 | + |
481 | +.PHONY: run |
482 | +run: build instance |
483 | + bin/start-schooltool-instance instance |
484 | + |
485 | +.PHONY: tags |
486 | +tags: build |
487 | + bin/tags |
488 | + |
489 | +.PHONY: clean |
490 | +clean: |
491 | + rm -rf python |
492 | + rm -rf bin develop-eggs parts .installed.cfg |
493 | + rm -rf build |
494 | + rm -f ID TAGS tags |
495 | + rm -rf coverage ftest-coverage |
496 | + find . -name '*.py[co]' -delete |
497 | + find . -name '*.mo' -delete |
498 | + find . -name 'LC_MESSAGES' -exec rmdir -p --ignore-fail-on-non-empty {} + |
499 | + |
500 | +.PHONY: realclean |
501 | +realclean: |
502 | + rm -rf eggs |
503 | + rm -rf dist |
504 | + rm -rf instance |
505 | + $(MAKE) clean |
506 | + |
507 | +# Tests |
508 | + |
509 | +.PHONY: test |
510 | +test: build |
511 | + bin/test -u |
512 | + |
513 | +.PHONY: ftest |
514 | +ftest: build |
515 | + bin/test -f |
516 | + |
517 | +.PHONY: testall |
518 | +testall: build |
519 | + bin/test --at-level 2 |
520 | + |
521 | +# Coverage |
522 | + |
523 | +.PHONY: coverage |
524 | +coverage: build |
525 | + rm -rf coverage |
526 | + bin/test --at-level 2 -u --coverage=$(CURDIR)/coverage |
527 | + |
528 | +.PHONY: coverage-reports-html |
529 | +coverage-reports-html coverage/reports: build |
530 | + test -d coverage || $(MAKE) coverage |
531 | + rm -rf coverage/reports |
532 | + mkdir coverage/reports |
533 | + bin/coverage coverage coverage/reports |
534 | + ln -s $(PACKAGE).html coverage/reports/index.html |
535 | + |
536 | +.PHONY: ftest-coverage |
537 | +ftest-coverage: build |
538 | + rm -rf ftest-coverage |
539 | + bin/test --at-level 2 -f --coverage=$(CURDIR)/ftest-coverage |
540 | + |
541 | +.PHONY: ftest-coverage-reports-html |
542 | +ftest-coverage-reports-html ftest-coverage/reports: build |
543 | + test -d ftest-coverage || $(MAKE) ftest-coverage |
544 | + rm -rf ftest-coverage/reports |
545 | + mkdir ftest-coverage/reports |
546 | + bin/coverage ftest-coverage ftest-coverage/reports |
547 | + ln -s $(PACKAGE).html ftest-coverage/reports/index.html |
548 | + |
549 | +# Translations |
550 | + |
551 | +.PHONY: extract-translations |
552 | +extract-translations: build |
553 | + bin/i18nextract --egg $(PACKAGE) \ |
554 | + --domain $(PACKAGE) \ |
555 | + --zcml $(TRANSLATIONS_ZCML) \ |
556 | + --output-file $(LOCALES)/$(PACKAGE).pot |
557 | + |
558 | +.PHONY: compile-translations |
559 | +compile-translations: |
560 | + for f in $(LOCALES)/*.po; do \ |
561 | + mkdir -p $${f%.po}/LC_MESSAGES; \ |
562 | + msgfmt -o $${f%.po}/LC_MESSAGES/$(PACKAGE).mo $$f;\ |
563 | + done |
564 | + |
565 | +.PHONY: update-translations |
566 | +update-translations: |
567 | + for f in $(LOCALES)/*.po; do \ |
568 | + msgmerge -qUN $$f $(LOCALES)/$(PACKAGE).pot ;\ |
569 | + done |
570 | + $(MAKE) compile-translations |
571 | + |
572 | +# Release |
573 | + |
574 | +.PHONY: release |
575 | +release: compile-translations |
576 | + grep -qv 'dev' version.txt.in || echo -n `cat version.txt.in`-r`bzr revno` > version.txt |
577 | + $(PYTHON) setup.py sdist |
578 | + rm -f version.txt |
579 | + |
580 | +.PHONY: move-release |
581 | +move-release: upload |
582 | + rm -v dist/$(PACKAGE)-*dev-r*.tar.gz |
583 | + |
584 | +.PHONY: upload |
585 | +upload: |
586 | + @VERSION=`cat version.txt.in` ;\ |
587 | + DIST=$(DIST) ;\ |
588 | + grep -qv 'dev' version.txt.in || VERSION=`cat version.txt.in`-r`bzr revno` ;\ |
589 | + grep -qv 'dev' version.txt.in || DIST=$(DIST)/dev ;\ |
590 | + if [ -w $${DIST} ] ; then \ |
591 | + echo cp dist/$(PACKAGE)-$${VERSION}.tar.gz $${DIST} ;\ |
592 | + cp dist/$(PACKAGE)-$${VERSION}.tar.gz $${DIST} ;\ |
593 | + else \ |
594 | + echo scp dist/$(PACKAGE)-$${VERSION}.tar.gz* schooltool.org:$${DIST} ;\ |
595 | + scp dist/$(PACKAGE)-$${VERSION}.tar.gz* schooltool.org:$${DIST} ;\ |
596 | + fi |
597 | + |
598 | +# Helpers |
599 | + |
600 | +.PHONY: ubuntu-environment |
601 | +ubuntu-environment: |
602 | + sudo apt-get install bzr build-essential gettext enscript ttf-liberation \ |
603 | + python-all-dev python-virtualenv \ |
604 | + libicu-dev libxslt1-dev libfreetype6-dev libjpeg62-dev |
605 | + |
606 | |
607 | === renamed file 'Makefile' => 'Makefile.moved' |
608 | === added file 'README.txt' |
609 | === renamed file 'README.txt' => 'README.txt.moved' |
610 | === added file 'base.cfg' |
611 | --- base.cfg 1970-01-01 00:00:00 +0000 |
612 | +++ base.cfg 2012-10-08 23:48:26 +0000 |
613 | @@ -0,0 +1,37 @@ |
614 | +[buildout] |
615 | +extends = http://ftp.schooltool.org/schooltool/flourish/versions.cfg |
616 | +versions = versions |
617 | +find-links = |
618 | + http://ftp.schooltool.org/schooltool/flourish/ |
619 | + http://ftp.schooltool.org/schooltool/flourish/dev/ |
620 | +parts = package test scripts tags omelette |
621 | + |
622 | +[package] |
623 | +recipe = zc.recipe.egg:eggs |
624 | +unzip = true |
625 | +# set eggs to package name in buildout.cfg |
626 | +eggs = |
627 | + |
628 | +[scripts] |
629 | +recipe = zc.recipe.egg:scripts |
630 | +unzip = true |
631 | +eggs = |
632 | + ${package:eggs} |
633 | + schooltool |
634 | + schooltool.devtools |
635 | + z3c.coverage |
636 | +interpreter = python |
637 | + |
638 | +[test] |
639 | +recipe = schooltool.devtools:testrunner |
640 | +eggs = ${package:eggs} |
641 | +defaults = ['--tests-pattern', '^f?s?tests$', '-v', '--auto-color'] |
642 | + |
643 | +[tags] |
644 | +recipe = z3c.recipe.tag:tags |
645 | +eggs = ${test:eggs} |
646 | + |
647 | +[omelette] |
648 | +recipe = collective.recipe.omelette |
649 | +eggs = ${package:eggs} |
650 | +ignores = roman zope.ucol site |
651 | |
652 | === renamed file 'base.cfg' => 'base.cfg.moved' |
653 | === added file 'bootstrap.py' |
654 | --- bootstrap.py 1970-01-01 00:00:00 +0000 |
655 | +++ bootstrap.py 2012-10-08 23:48:26 +0000 |
656 | @@ -0,0 +1,260 @@ |
657 | +############################################################################## |
658 | +# |
659 | +# Copyright (c) 2006 Zope Foundation and Contributors. |
660 | +# All Rights Reserved. |
661 | +# |
662 | +# This software is subject to the provisions of the Zope Public License, |
663 | +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. |
664 | +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED |
665 | +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
666 | +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS |
667 | +# FOR A PARTICULAR PURPOSE. |
668 | +# |
669 | +############################################################################## |
670 | +"""Bootstrap a buildout-based project |
671 | + |
672 | +Simply run this script in a directory containing a buildout.cfg. |
673 | +The script accepts buildout command-line options, so you can |
674 | +use the -c option to specify an alternate configuration file. |
675 | +""" |
676 | + |
677 | +import os, shutil, sys, tempfile, textwrap, urllib, urllib2, subprocess |
678 | +from optparse import OptionParser |
679 | + |
680 | +if sys.platform == 'win32': |
681 | + def quote(c): |
682 | + if ' ' in c: |
683 | + return '"%s"' % c # work around spawn lamosity on windows |
684 | + else: |
685 | + return c |
686 | +else: |
687 | + quote = str |
688 | + |
689 | +# See zc.buildout.easy_install._has_broken_dash_S for motivation and comments. |
690 | +stdout, stderr = subprocess.Popen( |
691 | + [sys.executable, '-Sc', |
692 | + 'try:\n' |
693 | + ' import ConfigParser\n' |
694 | + 'except ImportError:\n' |
695 | + ' print 1\n' |
696 | + 'else:\n' |
697 | + ' print 0\n'], |
698 | + stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() |
699 | +has_broken_dash_S = bool(int(stdout.strip())) |
700 | + |
701 | +# In order to be more robust in the face of system Pythons, we want to |
702 | +# run without site-packages loaded. This is somewhat tricky, in |
703 | +# particular because Python 2.6's distutils imports site, so starting |
704 | +# with the -S flag is not sufficient. However, we'll start with that: |
705 | +if not has_broken_dash_S and 'site' in sys.modules: |
706 | + # We will restart with python -S. |
707 | + args = sys.argv[:] |
708 | + args[0:0] = [sys.executable, '-S'] |
709 | + args = map(quote, args) |
710 | + os.execv(sys.executable, args) |
711 | +# Now we are running with -S. We'll get the clean sys.path, import site |
712 | +# because distutils will do it later, and then reset the path and clean |
713 | +# out any namespace packages from site-packages that might have been |
714 | +# loaded by .pth files. |
715 | +clean_path = sys.path[:] |
716 | +import site |
717 | +sys.path[:] = clean_path |
718 | +for k, v in sys.modules.items(): |
719 | + if k in ('setuptools', 'pkg_resources') or ( |
720 | + hasattr(v, '__path__') and |
721 | + len(v.__path__)==1 and |
722 | + not os.path.exists(os.path.join(v.__path__[0],'__init__.py'))): |
723 | + # This is a namespace package. Remove it. |
724 | + sys.modules.pop(k) |
725 | + |
726 | +is_jython = sys.platform.startswith('java') |
727 | + |
728 | +setuptools_source = 'http://peak.telecommunity.com/dist/ez_setup.py' |
729 | +distribute_source = 'http://python-distribute.org/distribute_setup.py' |
730 | + |
731 | +# parsing arguments |
732 | +def normalize_to_url(option, opt_str, value, parser): |
733 | + if value: |
734 | + if '://' not in value: # It doesn't smell like a URL. |
735 | + value = 'file://%s' % ( |
736 | + urllib.pathname2url( |
737 | + os.path.abspath(os.path.expanduser(value))),) |
738 | + if opt_str == '--download-base' and not value.endswith('/'): |
739 | + # Download base needs a trailing slash to make the world happy. |
740 | + value += '/' |
741 | + else: |
742 | + value = None |
743 | + name = opt_str[2:].replace('-', '_') |
744 | + setattr(parser.values, name, value) |
745 | + |
746 | +usage = '''\ |
747 | +[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options] |
748 | + |
749 | +Bootstraps a buildout-based project. |
750 | + |
751 | +Simply run this script in a directory containing a buildout.cfg, using the |
752 | +Python that you want bin/buildout to use. |
753 | + |
754 | +Note that by using --setup-source and --download-base to point to |
755 | +local resources, you can keep this script from going over the network. |
756 | +''' |
757 | + |
758 | +parser = OptionParser(usage=usage) |
759 | +parser.add_option("-v", "--version", dest="version", |
760 | + help="use a specific zc.buildout version") |
761 | +parser.add_option("-d", "--distribute", |
762 | + action="store_true", dest="use_distribute", default=False, |
763 | + help="Use Distribute rather than Setuptools.") |
764 | +parser.add_option("--setup-source", action="callback", dest="setup_source", |
765 | + callback=normalize_to_url, nargs=1, type="string", |
766 | + help=("Specify a URL or file location for the setup file. " |
767 | + "If you use Setuptools, this will default to " + |
768 | + setuptools_source + "; if you use Distribute, this " |
769 | + "will default to " + distribute_source +".")) |
770 | +parser.add_option("--download-base", action="callback", dest="download_base", |
771 | + callback=normalize_to_url, nargs=1, type="string", |
772 | + help=("Specify a URL or directory for downloading " |
773 | + "zc.buildout and either Setuptools or Distribute. " |
774 | + "Defaults to PyPI.")) |
775 | +parser.add_option("--eggs", |
776 | + help=("Specify a directory for storing eggs. Defaults to " |
777 | + "a temporary directory that is deleted when the " |
778 | + "bootstrap script completes.")) |
779 | +parser.add_option("-t", "--accept-buildout-test-releases", |
780 | + dest='accept_buildout_test_releases', |
781 | + action="store_true", default=False, |
782 | + help=("Normally, if you do not specify a --version, the " |
783 | + "bootstrap script and buildout gets the newest " |
784 | + "*final* versions of zc.buildout and its recipes and " |
785 | + "extensions for you. If you use this flag, " |
786 | + "bootstrap and buildout will get the newest releases " |
787 | + "even if they are alphas or betas.")) |
788 | +parser.add_option("-c", None, action="store", dest="config_file", |
789 | + help=("Specify the path to the buildout configuration " |
790 | + "file to be used.")) |
791 | + |
792 | +options, args = parser.parse_args() |
793 | + |
794 | +# if -c was provided, we push it back into args for buildout's main function |
795 | +if options.config_file is not None: |
796 | + args += ['-c', options.config_file] |
797 | + |
798 | +if options.eggs: |
799 | + eggs_dir = os.path.abspath(os.path.expanduser(options.eggs)) |
800 | +else: |
801 | + eggs_dir = tempfile.mkdtemp() |
802 | + |
803 | +if options.setup_source is None: |
804 | + if options.use_distribute: |
805 | + options.setup_source = distribute_source |
806 | + else: |
807 | + options.setup_source = setuptools_source |
808 | + |
809 | +if options.accept_buildout_test_releases: |
810 | + args.append('buildout:accept-buildout-test-releases=true') |
811 | +args.append('bootstrap') |
812 | + |
813 | +try: |
814 | + import pkg_resources |
815 | + import setuptools # A flag. Sometimes pkg_resources is installed alone. |
816 | + if not hasattr(pkg_resources, '_distribute'): |
817 | + raise ImportError |
818 | +except ImportError: |
819 | + ez_code = urllib2.urlopen( |
820 | + options.setup_source).read().replace('\r\n', '\n') |
821 | + ez = {} |
822 | + exec ez_code in ez |
823 | + setup_args = dict(to_dir=eggs_dir, download_delay=0) |
824 | + if options.download_base: |
825 | + setup_args['download_base'] = options.download_base |
826 | + if options.use_distribute: |
827 | + setup_args['no_fake'] = True |
828 | + ez['use_setuptools'](**setup_args) |
829 | + if 'pkg_resources' in sys.modules: |
830 | + reload(sys.modules['pkg_resources']) |
831 | + import pkg_resources |
832 | + # This does not (always?) update the default working set. We will |
833 | + # do it. |
834 | + for path in sys.path: |
835 | + if path not in pkg_resources.working_set.entries: |
836 | + pkg_resources.working_set.add_entry(path) |
837 | + |
838 | +cmd = [quote(sys.executable), |
839 | + '-c', |
840 | + quote('from setuptools.command.easy_install import main; main()'), |
841 | + '-mqNxd', |
842 | + quote(eggs_dir)] |
843 | + |
844 | +if not has_broken_dash_S: |
845 | + cmd.insert(1, '-S') |
846 | + |
847 | +find_links = options.download_base |
848 | +if not find_links: |
849 | + find_links = os.environ.get('bootstrap-testing-find-links') |
850 | +if find_links: |
851 | + cmd.extend(['-f', quote(find_links)]) |
852 | + |
853 | +if options.use_distribute: |
854 | + setup_requirement = 'distribute' |
855 | +else: |
856 | + setup_requirement = 'setuptools' |
857 | +ws = pkg_resources.working_set |
858 | +setup_requirement_path = ws.find( |
859 | + pkg_resources.Requirement.parse(setup_requirement)).location |
860 | +env = dict( |
861 | + os.environ, |
862 | + PYTHONPATH=setup_requirement_path) |
863 | + |
864 | +requirement = 'zc.buildout' |
865 | +version = options.version |
866 | +if version is None and not options.accept_buildout_test_releases: |
867 | + # Figure out the most recent final version of zc.buildout. |
868 | + import setuptools.package_index |
869 | + _final_parts = '*final-', '*final' |
870 | + def _final_version(parsed_version): |
871 | + for part in parsed_version: |
872 | + if (part[:1] == '*') and (part not in _final_parts): |
873 | + return False |
874 | + return True |
875 | + index = setuptools.package_index.PackageIndex( |
876 | + search_path=[setup_requirement_path]) |
877 | + if find_links: |
878 | + index.add_find_links((find_links,)) |
879 | + req = pkg_resources.Requirement.parse(requirement) |
880 | + if index.obtain(req) is not None: |
881 | + best = [] |
882 | + bestv = None |
883 | + for dist in index[req.project_name]: |
884 | + distv = dist.parsed_version |
885 | + if _final_version(distv): |
886 | + if bestv is None or distv > bestv: |
887 | + best = [dist] |
888 | + bestv = distv |
889 | + elif distv == bestv: |
890 | + best.append(dist) |
891 | + if best: |
892 | + best.sort() |
893 | + version = best[-1].version |
894 | +if version: |
895 | + requirement = '=='.join((requirement, version)) |
896 | +cmd.append(requirement) |
897 | + |
898 | +if is_jython: |
899 | + import subprocess |
900 | + exitcode = subprocess.Popen(cmd, env=env).wait() |
901 | +else: # Windows prefers this, apparently; otherwise we would prefer subprocess |
902 | + exitcode = os.spawnle(*([os.P_WAIT, sys.executable] + cmd + [env])) |
903 | +if exitcode != 0: |
904 | + sys.stdout.flush() |
905 | + sys.stderr.flush() |
906 | + print ("An error occurred when trying to install zc.buildout. " |
907 | + "Look above this message for any errors that " |
908 | + "were output by easy_install.") |
909 | + sys.exit(exitcode) |
910 | + |
911 | +ws.add_entry(eggs_dir) |
912 | +ws.require(requirement) |
913 | +import zc.buildout.buildout |
914 | +zc.buildout.buildout.main(args) |
915 | +if not options.eggs: # clean up temporary egg directory |
916 | + shutil.rmtree(eggs_dir) |
917 | |
918 | === renamed file 'bootstrap.py' => 'bootstrap.py.moved' |
919 | === added file 'buildout.cfg' |
920 | --- buildout.cfg 1970-01-01 00:00:00 +0000 |
921 | +++ buildout.cfg 2012-10-08 23:48:26 +0000 |
922 | @@ -0,0 +1,13 @@ |
923 | +[buildout] |
924 | +extends = base.cfg |
925 | +develop = . |
926 | + |
927 | +[versions] |
928 | +# Unset versions of packages you want to develop |
929 | +schooltool.virginia = |
930 | + |
931 | +[package] |
932 | +eggs = schooltool.virginia |
933 | + |
934 | +[test] |
935 | +eggs = schooltool.virginia [test] |
936 | |
937 | === renamed file 'buildout.cfg' => 'buildout.cfg.moved' |
938 | === added file 'setup.py' |
939 | --- setup.py 1970-01-01 00:00:00 +0000 |
940 | +++ setup.py 2012-10-08 23:48:26 +0000 |
941 | @@ -0,0 +1,76 @@ |
942 | +#!/usr/bin/env python |
943 | +# |
944 | +# SchoolTool - common information systems platform for school administration |
945 | +# Copyright (c) 2008, 2009, 2010, 2011 Shuttleworth Foundation, |
946 | +# |
947 | +# This program is free software; you can redistribute it and/or modify |
948 | +# it under the terms of the GNU General Public License as published by |
949 | +# the Free Software Foundation; either version 2 of the License, or |
950 | +# (at your option) any later version. |
951 | +# |
952 | +# This program is distributed in the hope that it will be useful, |
953 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
954 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
955 | +# GNU General Public License for more details. |
956 | +# |
957 | +# You should have received a copy of the GNU General Public License along |
958 | +# with this program; if not, write to the Free Software Foundation, Inc., |
959 | +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
960 | +# |
961 | +""" |
962 | +SchoolTool Virginia setup script. |
963 | +""" |
964 | + |
965 | +import os |
966 | +from setuptools import setup, find_packages |
967 | + |
968 | +if os.path.exists("version.txt"): |
969 | + version = open("version.txt").read().strip() |
970 | +else: |
971 | + version = open("version.txt.in").read().strip() |
972 | + |
973 | +def read(*rnames): |
974 | + text = open(os.path.join(os.path.dirname(__file__), *rnames)).read() |
975 | + return text |
976 | + |
977 | +setup( |
978 | + name="schooltool.virginia", |
979 | + description="Virginia plugin for SchoolTool", |
980 | + long_description=( |
981 | + read('README.txt') |
982 | + + '\n\n' + |
983 | + read('CHANGES.txt') |
984 | + ), |
985 | + version=version, |
986 | + url='http://www.schooltool.org', |
987 | + license="GPL", |
988 | + maintainer="SchoolTool Developers", |
989 | + maintainer_email="schooltool-developers@lists.launchpad.net", |
990 | + platforms=["any"], |
991 | + classifiers=["Development Status :: 4 - Beta", |
992 | + "Environment :: Web Environment", |
993 | + "Intended Audience :: End Users/Desktop", |
994 | + "License :: OSI Approved :: GNU General Public License (GPL)", |
995 | + "Operating System :: OS Independent", |
996 | + "Programming Language :: Python", |
997 | + "Programming Language :: Python :: 2.5", |
998 | + "Programming Language :: Python :: 2.6", |
999 | + "Programming Language :: Python :: 2.7", |
1000 | + "Programming Language :: Zope", |
1001 | + "Topic :: Education"], |
1002 | + package_dir={'': 'src'}, |
1003 | + packages=find_packages('src'), |
1004 | + namespace_packages=["schooltool"], |
1005 | + install_requires=['schooltool>=2.0', |
1006 | + 'schooltool.cando', |
1007 | + 'setuptools', |
1008 | + 'zope.i18n', |
1009 | + 'zope.i18nmessageid'], |
1010 | + extras_require={'test': ['schooltool.devtools>=0.6']}, |
1011 | + include_package_data=True, |
1012 | + zip_safe=False, |
1013 | + entry_points=""" |
1014 | + [z3c.autoinclude.plugin] |
1015 | + target = schooltool |
1016 | + """, |
1017 | + ) |
1018 | |
1019 | === renamed file 'setup.py' => 'setup.py.moved' |
1020 | === added directory 'src' |
1021 | === renamed directory 'src' => 'src.moved' |
1022 | === added directory 'src/schooltool' |
1023 | === added file 'src/schooltool/__init__.py' |
1024 | --- src/schooltool/__init__.py 1970-01-01 00:00:00 +0000 |
1025 | +++ src/schooltool/__init__.py 2012-10-08 23:48:26 +0000 |
1026 | @@ -0,0 +1,1 @@ |
1027 | +__import__('pkg_resources').declare_namespace(__name__) |
1028 | |
1029 | === added directory 'src/schooltool/virginia' |
1030 | === added file 'src/schooltool/virginia/__init__.py' |
1031 | --- src/schooltool/virginia/__init__.py 1970-01-01 00:00:00 +0000 |
1032 | +++ src/schooltool/virginia/__init__.py 2012-10-08 23:48:26 +0000 |
1033 | @@ -0,0 +1,22 @@ |
1034 | +# |
1035 | +# SchoolTool - common information systems platform for school administration |
1036 | +# Copyright (c) 2012 Shuttleworth Foundation |
1037 | +# |
1038 | +# This program is free software; you can redistribute it and/or modify |
1039 | +# it under the terms of the GNU General Public License as published by |
1040 | +# the Free Software Foundation; either version 2 of the License, or |
1041 | +# (at your option) any later version. |
1042 | +# |
1043 | +# This program is distributed in the hope that it will be useful, |
1044 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1045 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1046 | +# GNU General Public License for more details. |
1047 | +# |
1048 | +# You should have received a copy of the GNU General Public License |
1049 | +# along with this program; if not, write to the Free Software |
1050 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
1051 | +# |
1052 | + |
1053 | +from zope.i18nmessageid import MessageFactory |
1054 | +VirginiaMessage = MessageFactory("schooltool.virginia") |
1055 | + |
1056 | |
1057 | === added directory 'src/schooltool/virginia/browser' |
1058 | === added file 'src/schooltool/virginia/browser/__init__.py' |
1059 | --- src/schooltool/virginia/browser/__init__.py 1970-01-01 00:00:00 +0000 |
1060 | +++ src/schooltool/virginia/browser/__init__.py 2012-10-08 23:48:26 +0000 |
1061 | @@ -0,0 +1,1 @@ |
1062 | +# Make a package |
1063 | |
1064 | === added file 'src/schooltool/virginia/browser/configure.zcml' |
1065 | --- src/schooltool/virginia/browser/configure.zcml 1970-01-01 00:00:00 +0000 |
1066 | +++ src/schooltool/virginia/browser/configure.zcml 2012-10-08 23:48:26 +0000 |
1067 | @@ -0,0 +1,357 @@ |
1068 | +<?xml version="1.0"?> |
1069 | +<configure xmlns="http://namespaces.zope.org/browser" |
1070 | + xmlns:zope="http://namespaces.zope.org/zope" |
1071 | + xmlns:flourish="http://schooltool.org/flourish" |
1072 | + xmlns:report="http://namespaces.schooltool.org/report" |
1073 | + i18n_domain="schooltool.virgina"> |
1074 | + |
1075 | + <zope:resourceLibrary |
1076 | + name="schooltool.virginia.flourish" |
1077 | + require="schooltool.skin.flourish" |
1078 | + layer="schooltool.skin.flourish.IFlourishLayer"> |
1079 | + <directory |
1080 | + source="resources" |
1081 | + include="d3.v2.min.js |
1082 | + virginia.js |
1083 | + virginia.css" |
1084 | + /> |
1085 | + </zope:resourceLibrary> |
1086 | + |
1087 | + <flourish:viewlet |
1088 | + name="import-va-sections.html" |
1089 | + after="import.html" |
1090 | + before="import-skills.html" |
1091 | + title="Import VA Sections" |
1092 | + manager="schooltool.app.browser.app.SchoolActionsLinks" |
1093 | + class="schooltool.virginia.browser.importer.ImportSectionsLinkViewlet" |
1094 | + permission="schooltool.edit" |
1095 | + /> |
1096 | + |
1097 | + <configure package="schooltool.export"> |
1098 | + |
1099 | + <flourish:page |
1100 | + name="import-va-sections.html" |
1101 | + subtitle="Import VA Sections" |
1102 | + for="schooltool.schoolyear.interfaces.ISchoolYear" |
1103 | + content_template="templates/f_import.pt" |
1104 | + class="schooltool.virginia.browser.importer.VirginiaSectionsMegaImporter" |
1105 | + permission="schooltool.edit" /> |
1106 | + |
1107 | + </configure> |
1108 | + |
1109 | + <report:reportLink |
1110 | + name="b_virginia_section_report" |
1111 | + view="schooltool.cando.browser.gradebook.SkillsGradebookOverview" |
1112 | + layer="schooltool.skin.flourish.IFlourishLayer" |
1113 | + permission="schooltool.edit" |
1114 | + group="Section" |
1115 | + description="XXX Report description XXX" |
1116 | + title="VA Section Report" |
1117 | + link="virginia_section_report.html" |
1118 | + /> |
1119 | + |
1120 | + <flourish:page_breadcrumbs |
1121 | + show_page_title="false" |
1122 | + title="Virginia Section Report" |
1123 | + view=".report.VASectionReportView" |
1124 | + url="" |
1125 | + for="schooltool.cando.interfaces.ISkillsGradebook" |
1126 | + /> |
1127 | + |
1128 | + <flourish:activeViewlet |
1129 | + name="cando" |
1130 | + manager="schooltool.skin.flourish.page.IHeaderNavigationManager" |
1131 | + view=".report.VASectionReportView" |
1132 | + /> |
1133 | + |
1134 | + <flourish:page |
1135 | + name="virginia_section_report.html" |
1136 | + for="schooltool.cando.interfaces.ISkillsGradebook" |
1137 | + class=".report.VASectionReportView" |
1138 | + title="Virginia Section Report" |
1139 | + permission="schooltool.view" |
1140 | + /> |
1141 | + |
1142 | + <flourish:viewlet |
1143 | + name="course-details" |
1144 | + view=".report.VASectionReportView" |
1145 | + manager="schooltool.skin.flourish.page.IPageContentManager" |
1146 | + class=".report.VAReportDetailsViewlet" |
1147 | + permission="schooltool.view" |
1148 | + render="template" |
1149 | + /> |
1150 | + |
1151 | + <flourish:viewlet |
1152 | + name="passing-score-filter" |
1153 | + after="course-details" |
1154 | + view=".report.VASectionReportView" |
1155 | + manager="schooltool.skin.flourish.page.IPageContentManager" |
1156 | + class=".report.VASectionReportPassingScoreViewlet" |
1157 | + permission="schooltool.view" |
1158 | + render="template" |
1159 | + /> |
1160 | + |
1161 | + <flourish:viewlet |
1162 | + name="report-tables" |
1163 | + after="passing-score-filter" |
1164 | + view=".report.VASectionReportView" |
1165 | + manager="schooltool.skin.flourish.page.IPageContentManager" |
1166 | + class=".report.VASectionReportTablesViewlet" |
1167 | + permission="schooltool.view" |
1168 | + render="template" |
1169 | + /> |
1170 | + |
1171 | + <flourish:viewlet |
1172 | + name="done-button" |
1173 | + after="report-tables" |
1174 | + view=".report.VASectionReportView" |
1175 | + manager="schooltool.skin.flourish.page.IPageContentManager" |
1176 | + class=".report.SectionReportDoneButtonViewlet" |
1177 | + permission="schooltool.view" |
1178 | + render="template" |
1179 | + /> |
1180 | + |
1181 | + <flourish:breadcrumb |
1182 | + for="schooltool.cando.interfaces.ICourseSkillSet" |
1183 | + view=".report.VASectionReportView" |
1184 | + title="" |
1185 | + /> |
1186 | + |
1187 | + <flourish:page |
1188 | + name="virginia_section_report_chart.html" |
1189 | + for="schooltool.cando.interfaces.ISkillsGradebook" |
1190 | + class=".report.VASectionChartReportView" |
1191 | + title="Virginia Section Chart Report + Chart" |
1192 | + permission="schooltool.view" |
1193 | + /> |
1194 | + |
1195 | + <flourish:breadcrumb |
1196 | + for="schooltool.cando.interfaces.ICourseSkillSet" |
1197 | + view=".report.VASectionChartReportView" |
1198 | + title="" |
1199 | + /> |
1200 | + |
1201 | + <flourish:activeViewlet |
1202 | + name="cando" |
1203 | + manager="schooltool.skin.flourish.page.IHeaderNavigationManager" |
1204 | + view=".report.SectionReportByStudentView" |
1205 | + /> |
1206 | + |
1207 | + <flourish:page |
1208 | + name="section_report_by_student.html" |
1209 | + for="schooltool.cando.interfaces.ISkillsGradebook" |
1210 | + class=".report.SectionReportByStudentView" |
1211 | + title="Section Summary" |
1212 | + permission="schooltool.view" |
1213 | + /> |
1214 | + |
1215 | + <flourish:page |
1216 | + name="section_report_by_skill.html" |
1217 | + for="schooltool.cando.interfaces.ISkillsGradebook" |
1218 | + class=".report.SectionReportBySkillView" |
1219 | + title="Section Report By Skill" |
1220 | + permission="schooltool.view" |
1221 | + /> |
1222 | + |
1223 | + <flourish:breadcrumb |
1224 | + for="schooltool.cando.interfaces.ICourseSkillSet" |
1225 | + view=".report.SectionReportView" |
1226 | + title="" |
1227 | + /> |
1228 | + |
1229 | + <flourish:viewlet |
1230 | + name="script" |
1231 | + before="*" |
1232 | + view=".report.SectionReportView" |
1233 | + manager="schooltool.skin.flourish.page.IPageContentManager" |
1234 | + template="templates/section_report_script.pt" |
1235 | + permission="schooltool.view" |
1236 | + /> |
1237 | + |
1238 | + <flourish:viewlet |
1239 | + name="course-details" |
1240 | + view=".report.SectionReportView" |
1241 | + manager="schooltool.skin.flourish.page.IPageContentManager" |
1242 | + class=".report.ReportDetailsViewlet" |
1243 | + permission="schooltool.view" |
1244 | + render="template" |
1245 | + /> |
1246 | + |
1247 | + <flourish:viewlet |
1248 | + name="report-description" |
1249 | + class=".report.SectionReportByStudentDescriptionViewlet" |
1250 | + view=".report.SectionReportByStudentView" |
1251 | + manager="schooltool.skin.flourish.page.IPageRelatedManager" |
1252 | + permission="schooltool.view" |
1253 | + render="template" |
1254 | + /> |
1255 | + |
1256 | + <flourish:viewlet |
1257 | + name="score-colors" |
1258 | + after="report-description" |
1259 | + class=".report.ScoreColorsViewlet" |
1260 | + view=".report.SectionReportByStudentView" |
1261 | + manager="schooltool.skin.flourish.page.IPageRelatedManager" |
1262 | + permission="schooltool.view" |
1263 | + render="template" |
1264 | + /> |
1265 | + |
1266 | + <flourish:viewlet |
1267 | + name="score_colors_table" |
1268 | + class=".report.ScoreColorsTable" |
1269 | + for="schooltool.cando.interfaces.ISkillsGradebook" |
1270 | + manager="schooltool.skin.flourish.interfaces.IAJAXParts" |
1271 | + view=".report.SectionReportByStudentView" |
1272 | + permission="schooltool.view" |
1273 | + /> |
1274 | + |
1275 | + <flourish:viewlet |
1276 | + name="filter" |
1277 | + class="schooltool.table.table.DoNotFilter" |
1278 | + manager=".report.ScoreColorsTable" |
1279 | + permission="schooltool.view" |
1280 | + /> |
1281 | + |
1282 | + <flourish:viewlet |
1283 | + name="report-description" |
1284 | + after="course-details" |
1285 | + view=".report.SectionReportBySkillView" |
1286 | + manager="schooltool.skin.flourish.page.IPageContentManager" |
1287 | + class=".report.SectionReportBySkillDescriptionViewlet" |
1288 | + permission="schooltool.view" |
1289 | + render="template" |
1290 | + /> |
1291 | + |
1292 | + <flourish:viewlet |
1293 | + name="report-table" |
1294 | + after="report-description" |
1295 | + view=".report.SectionReportView" |
1296 | + manager="schooltool.skin.flourish.page.IPageContentManager" |
1297 | + class=".report.SectionReportTableViewlet" |
1298 | + permission="schooltool.view" |
1299 | + render="template" |
1300 | + /> |
1301 | + |
1302 | + <flourish:viewlet |
1303 | + name="done-button" |
1304 | + after="report-table" |
1305 | + view=".report.SectionReportView" |
1306 | + manager="schooltool.skin.flourish.page.IPageContentManager" |
1307 | + class=".report.SectionReportDoneButtonViewlet" |
1308 | + permission="schooltool.view" |
1309 | + render="template" |
1310 | + /> |
1311 | + |
1312 | + <flourish:viewlet |
1313 | + name="charts_table" |
1314 | + class=".report.ByStudentChartsTable" |
1315 | + for="schooltool.course.interfaces.ISection" |
1316 | + manager="schooltool.skin.flourish.interfaces.IAJAXParts" |
1317 | + view=".report.SectionReportByStudentView" |
1318 | + permission="schooltool.view" |
1319 | + /> |
1320 | + |
1321 | + <flourish:viewlet |
1322 | + name="charts_table" |
1323 | + class=".report.BySkillChartsTable" |
1324 | + for="schooltool.course.interfaces.ISection" |
1325 | + manager="schooltool.skin.flourish.interfaces.IAJAXParts" |
1326 | + view=".report.SectionReportBySkillView" |
1327 | + permission="schooltool.view" |
1328 | + /> |
1329 | + |
1330 | + <flourish:viewlet |
1331 | + name="filter" |
1332 | + class="schooltool.table.table.DoNotFilter" |
1333 | + manager=".report.ByStudentChartsTable" |
1334 | + permission="schooltool.view" |
1335 | + /> |
1336 | + |
1337 | + <flourish:viewlet |
1338 | + name="filter" |
1339 | + class="schooltool.table.table.DoNotFilter" |
1340 | + manager=".report.BySkillChartsTable" |
1341 | + permission="schooltool.view" |
1342 | + /> |
1343 | + |
1344 | + <flourish:viewlet |
1345 | + name="passing_score_filter" |
1346 | + manager="schooltool.skin.flourish.page.IPageRefineManager" |
1347 | + class=".report.PassingScoreFilterViewlet" |
1348 | + title="Passing Score" |
1349 | + permission="schooltool.view" |
1350 | + view=".report.SectionReportView" |
1351 | + /> |
1352 | + |
1353 | + <flourish:viewlet |
1354 | + name="passing_score_filter_menu" |
1355 | + manager=".report.PassingScoreFilterViewlet" |
1356 | + class=".report.PassingScoreFilterMenuViewlet" |
1357 | + permission="schooltool.view" |
1358 | + render="template" |
1359 | + /> |
1360 | + |
1361 | + <flourish:viewlet |
1362 | + name="skill_type_filter" |
1363 | + after="passing_target_filter" |
1364 | + manager="schooltool.skin.flourish.page.IPageRefineManager" |
1365 | + class=".report.SkillTypeFilterViewlet" |
1366 | + title="Type" |
1367 | + permission="schooltool.view" |
1368 | + view=".report.SectionReportView" |
1369 | + /> |
1370 | + |
1371 | + <flourish:viewlet |
1372 | + name="skill_type_filter_options" |
1373 | + manager=".report.SkillTypeFilterViewlet" |
1374 | + class=".report.SkillTypeFilterOptionsViewlet" |
1375 | + permission="schooltool.view" |
1376 | + render="template" |
1377 | + /> |
1378 | + |
1379 | + <flourish:viewlet |
1380 | + name="passing_target_filter" |
1381 | + after="passing_score_filter" |
1382 | + manager="schooltool.skin.flourish.page.IPageRefineManager" |
1383 | + class=".report.PassingTargetFilterViewlet" |
1384 | + title="% Passing Target" |
1385 | + permission="schooltool.view" |
1386 | + view=".report.SectionReportView" |
1387 | + /> |
1388 | + |
1389 | + <flourish:viewlet |
1390 | + name="passing_target_filter_menu" |
1391 | + manager=".report.PassingTargetFilterViewlet" |
1392 | + class=".report.PassingTargetFilterMenuViewlet" |
1393 | + permission="schooltool.view" |
1394 | + render="template" |
1395 | + /> |
1396 | + |
1397 | + <flourish:page_breadcrumbs |
1398 | + show_page_title="false" |
1399 | + title="Section Summary" |
1400 | + view=".report.SectionReportByStudentView" |
1401 | + url="" |
1402 | + for="schooltool.cando.interfaces.ISkillsGradebook" |
1403 | + /> |
1404 | + |
1405 | + <flourish:page_breadcrumbs |
1406 | + show_page_title="false" |
1407 | + title="Section Report By Skill" |
1408 | + view=".report.SectionReportBySkillView" |
1409 | + url="" |
1410 | + for="schooltool.cando.interfaces.ISkillsGradebook" |
1411 | + /> |
1412 | + |
1413 | + <report:reportLink |
1414 | + name="ab_section_summary" |
1415 | + view="schooltool.cando.browser.gradebook.SkillsGradebookOverview" |
1416 | + layer="schooltool.skin.flourish.IFlourishLayer" |
1417 | + permission="schooltool.view" |
1418 | + group="Section" |
1419 | + description="A graphic overview of student achievement of skill within the section." |
1420 | + title="Section Summary" |
1421 | + link="section_report_by_student.html" |
1422 | + /> |
1423 | + |
1424 | +</configure> |
1425 | |
1426 | === added file 'src/schooltool/virginia/browser/importer.py' |
1427 | --- src/schooltool/virginia/browser/importer.py 1970-01-01 00:00:00 +0000 |
1428 | +++ src/schooltool/virginia/browser/importer.py 2012-10-08 23:48:26 +0000 |
1429 | @@ -0,0 +1,240 @@ |
1430 | +# |
1431 | +# SchoolTool - common information systems platform for school administration |
1432 | +# Copyright (c) 2012 Shuttleworth Foundation |
1433 | +# |
1434 | +# This program is free software; you can redistribute it and/or modify |
1435 | +# it under the terms of the GNU General Public License as published by |
1436 | +# the Free Software Foundation; either version 2 of the License, or |
1437 | +# (at your option) any later version. |
1438 | +# |
1439 | +# This program is distributed in the hope that it will be useful, |
1440 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1441 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1442 | +# GNU General Public License for more details. |
1443 | +# |
1444 | +# You should have received a copy of the GNU General Public License |
1445 | +# along with this program; if not, write to the Free Software |
1446 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
1447 | +# |
1448 | +""" |
1449 | +Virginia importer. |
1450 | +""" |
1451 | +import xlrd |
1452 | + |
1453 | +from zope.security.proxy import removeSecurityProxy |
1454 | +from zope.traversing.browser.absoluteurl import absoluteURL |
1455 | + |
1456 | +from schooltool.app.interfaces import ISchoolToolApplication |
1457 | +from schooltool.basicperson.interfaces import IDemographics |
1458 | +from schooltool.course.interfaces import ICourseContainer, ISectionContainer |
1459 | +from schooltool.course.section import Section |
1460 | +from schooltool.group.interfaces import IGroupContainer |
1461 | +from schooltool.export import importer |
1462 | +from schooltool.skin import flourish |
1463 | +from schooltool.schoolyear.interfaces import ISchoolYearContainer |
1464 | + |
1465 | +from schooltool.common import SchoolToolMessage as _ |
1466 | + |
1467 | + |
1468 | +ERROR_INVALID_COURSE_ID = _("Provided Course ID was not found") |
1469 | +ERROR_INVALID_MARKING_PERIODS = _("Provided Marking Period IDs are not valid") |
1470 | +ERROR_INVALID_TEACHER_ID = _("Provided Teacher ID was not found") |
1471 | +ERROR_INVALID_STUDENT_ID = _("Provided Student ID was not found") |
1472 | +ERROR_INVALID_BUILDING_ID = _("Provided Building ID was not found") |
1473 | + |
1474 | + |
1475 | +class ImportSectionsLinkViewlet(flourish.page.LinkViewlet): |
1476 | + |
1477 | + @property |
1478 | + def url(self): |
1479 | + schoolyears = ISchoolYearContainer(self.context) |
1480 | + schoolyear = schoolyears.getActiveSchoolYear() |
1481 | + if 'schoolyear_id' in self.request: |
1482 | + schoolyear_id = self.request['schoolyear_id'] |
1483 | + schoolyear = schoolyears.get(schoolyear_id, schoolyear) |
1484 | + if schoolyear is None: |
1485 | + return '' |
1486 | + url = '%s/import-va-sections.html' % ( |
1487 | + absoluteURL(schoolyear, self.request)) |
1488 | + return url |
1489 | + |
1490 | + |
1491 | +class SectionsImporter(importer.ImporterBase): |
1492 | + |
1493 | + def import_data(self, wb): |
1494 | + app = ISchoolToolApplication(None) |
1495 | + self.persons_container = app['persons'] |
1496 | + self.resources_container = app['resources'] |
1497 | + |
1498 | + self.courses = {} |
1499 | + for course in ICourseContainer(self.context).values(): |
1500 | + course_id = course.course_id |
1501 | + if course_id is not None: |
1502 | + course_ids = [ID.strip() for ID in course_id.split(",")] |
1503 | + for ID in course_ids: |
1504 | + self.courses[ID] = course |
1505 | + |
1506 | + self.teachers = {} |
1507 | + group_container = IGroupContainer(self.context) |
1508 | + teacher_group = group_container.get('teachers') |
1509 | + if teacher_group: |
1510 | + for teacher in teacher_group.members: |
1511 | + demographics = IDemographics(teacher, None) |
1512 | + if demographics is None: |
1513 | + continue |
1514 | + teacher_id = demographics.get('ID') |
1515 | + if teacher_id is None: |
1516 | + continue |
1517 | + self.teachers[teacher_id] = teacher |
1518 | + |
1519 | + self.wb = wb |
1520 | + for self.sheet_name in self.wb.sheet_names(): |
1521 | + if 'SectionDetails' not in self.sheet_name: |
1522 | + continue |
1523 | + self.process() |
1524 | + |
1525 | + def process(self): |
1526 | + current_section_id = None |
1527 | + sections_by_term = [] |
1528 | + sh = self.sheet |
1529 | + |
1530 | + for row in range(1, sh.nrows): |
1531 | + if sh.cell_value(rowx=row, colx=0) == '': |
1532 | + break |
1533 | + |
1534 | + num_errors = len(self.errors) |
1535 | + course_id = self.getRequiredTextFromCell(sh, row, 0).strip() |
1536 | + section_index = self.getRequiredTextFromCell(sh, row, 1).strip() |
1537 | + description = self.getRequiredTextFromCell(sh, row, 2).strip() |
1538 | + building_id = self.getRequiredTextFromCell(sh, row, 3).strip() |
1539 | + marking_period = self.getRequiredTextFromCell(sh, row, 5).strip() |
1540 | + term_description = self.getTextFromCell(sh, row, 6).strip() |
1541 | + start_period = self.getRequiredTextFromCell(sh, row, 7).strip() |
1542 | + teacher_id = self.getRequiredTextFromCell(sh, row, 8).strip() |
1543 | + student_id = self.getRequiredTextFromCell(sh, row, 11).strip() |
1544 | + if num_errors < len(self.errors): |
1545 | + continue |
1546 | + |
1547 | + course = self.courses.get(course_id) |
1548 | + if course is None: |
1549 | + self.error(row, 1, ERROR_INVALID_COURSE_ID) |
1550 | + |
1551 | + building = self.resources_container.get(building_id) |
1552 | + if building is None: |
1553 | + self.error(row, 3, ERROR_INVALID_BUILDING_ID) |
1554 | + |
1555 | + parts = marking_period.split('-') |
1556 | + if len(parts) != 2: |
1557 | + self.error(row, 5, ERROR_INVALID_MARKING_PERIODS) |
1558 | + else: |
1559 | + first_term = self.context.get(parts[0]) |
1560 | + last_term = self.context.get(parts[1]) |
1561 | + if first_term is None or last_term is None: |
1562 | + self.error(row, 5, ERROR_INVALID_MARKING_PERIODS) |
1563 | + elif first_term.first > last_term.first: |
1564 | + self.error(row, 5, ERROR_INVALID_MARKING_PERIODS) |
1565 | + else: |
1566 | + terms = [term for term in self.context.values() |
1567 | + if (term.first >= first_term.first and |
1568 | + term.first <= last_term.first)] |
1569 | + |
1570 | + teacher = self.teachers.get(teacher_id) |
1571 | + if teacher is None: |
1572 | + self.error(row, 8, ERROR_INVALID_TEACHER_ID) |
1573 | + |
1574 | + student = self.persons_container.get(student_id) |
1575 | + if student is None: |
1576 | + self.error(row, 11, ERROR_INVALID_STUDENT_ID) |
1577 | + |
1578 | + if num_errors < len(self.errors): |
1579 | + continue |
1580 | + |
1581 | + section_id = '%s-%s-%s-%s' % (course_id, building_id, |
1582 | + start_period, section_index) |
1583 | + if section_id != current_section_id: |
1584 | + current_section_id = section_id |
1585 | + sections_by_term = self.getSectionsByTerm(section_id, course, |
1586 | + description, building, |
1587 | + terms, start_period, |
1588 | + term_description, |
1589 | + section_index) |
1590 | + |
1591 | + for section in sections_by_term: |
1592 | + if teacher not in section.instructors: |
1593 | + section.instructors.add(teacher) |
1594 | + if student not in section.members: |
1595 | + section.members.add(student) |
1596 | + |
1597 | + def getSectionsByTerm(self, section_id, course, description, building, |
1598 | + terms, start_period, term_description, section_index): |
1599 | + sections_by_term = [] |
1600 | + previous_section = None |
1601 | + terms_part = '' |
1602 | + if term_description: |
1603 | + terms_part = ', ' + term_description |
1604 | + |
1605 | + for term in terms: |
1606 | + if start_period.isdigit(): |
1607 | + period = 'P' + start_period |
1608 | + else: |
1609 | + period = start_period |
1610 | + title = '%s, %s%s, %s' % (description, building.title, |
1611 | + terms_part, period) |
1612 | + |
1613 | + sections_container = ISectionContainer(term) |
1614 | + if section_id in sections_container: |
1615 | + section = sections_container[section_id] |
1616 | + else: |
1617 | + section = Section(title=title, description=course.description) |
1618 | + sections_container[section_id] = section |
1619 | + if course not in section.courses: |
1620 | + section.courses.add(removeSecurityProxy(course)) |
1621 | + if building not in section.resources: |
1622 | + section.resources.add(removeSecurityProxy(building)) |
1623 | + |
1624 | + if previous_section is not None: |
1625 | + previous_section.next = section |
1626 | + previous_section = section |
1627 | + sections_by_term.append(section) |
1628 | + |
1629 | + return sections_by_term |
1630 | + |
1631 | + |
1632 | +class VirginiaSectionsMegaImporter(importer.FlourishMegaImporter): |
1633 | + |
1634 | + def nextURL(self): |
1635 | + url = absoluteURL(ISchoolToolApplication(None), self.request) |
1636 | + return url + '/manage' |
1637 | + |
1638 | + @property |
1639 | + def importers(self): |
1640 | + return [ |
1641 | + SectionsImporter, |
1642 | + ] |
1643 | + |
1644 | + def update(self): |
1645 | + if "UPDATE_CANCEL" in self.request: |
1646 | + self.request.response.redirect(self.nextURL()) |
1647 | + return |
1648 | + |
1649 | + if "UPDATE_SUBMIT" not in self.request: |
1650 | + return |
1651 | + |
1652 | + xlsfile = self.request.get('xlsfile', '') |
1653 | + if not xlsfile: |
1654 | + return |
1655 | + self.data_provided = True |
1656 | + |
1657 | + wb = xlrd.open_workbook(file_contents=xlsfile.read()) |
1658 | + |
1659 | + if wb is None: |
1660 | + return |
1661 | + |
1662 | + for importer in self.importers: |
1663 | + imp = importer(self.context, self.request) |
1664 | + imp.import_data(wb) |
1665 | + self.errors.extend(imp.errors) |
1666 | + |
1667 | + if not self.errors: |
1668 | + self.request.response.redirect(self.nextURL()) |
1669 | + |
1670 | |
1671 | === added file 'src/schooltool/virginia/browser/report.py' |
1672 | --- src/schooltool/virginia/browser/report.py 1970-01-01 00:00:00 +0000 |
1673 | +++ src/schooltool/virginia/browser/report.py 2012-10-08 23:48:26 +0000 |
1674 | @@ -0,0 +1,878 @@ |
1675 | +# |
1676 | +# SchoolTool - common information systems platform for school administration |
1677 | +# Copyright (c) 2012 Shuttleworth Foundation |
1678 | +# |
1679 | +# This program is free software; you can redistribute it and/or modify |
1680 | +# it under the terms of the GNU General Public License as published by |
1681 | +# the Free Software Foundation; either version 2 of the License, or |
1682 | +# (at your option) any later version. |
1683 | +# |
1684 | +# This program is distributed in the hope that it will be useful, |
1685 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1686 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1687 | +# GNU General Public License for more details. |
1688 | +# |
1689 | +# You should have received a copy of the GNU General Public License |
1690 | +# along with this program; if not, write to the Free Software |
1691 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
1692 | +# |
1693 | +""" |
1694 | +Virginia report views. |
1695 | +""" |
1696 | + |
1697 | +from zope.browserpage.viewpagetemplatefile import ViewPageTemplateFile |
1698 | +from zope.cachedescriptors.property import Lazy |
1699 | +from zope.i18n.interfaces.locales import ICollator |
1700 | +from zope.security.proxy import removeSecurityProxy |
1701 | +from zope.traversing.browser.absoluteurl import absoluteURL |
1702 | +from zc.table.column import GetterColumn |
1703 | +import zc.resourcelibrary |
1704 | + |
1705 | +from schooltool import table |
1706 | +from schooltool.basicperson.interfaces import IDemographics |
1707 | +from schooltool.cando.interfaces import ISectionSkills |
1708 | +from schooltool.cando.interfaces import ISkillsGradebook |
1709 | +from schooltool.cando.skill import querySkillScoreSystem |
1710 | +from schooltool.common.inlinept import InlineViewPageTemplate |
1711 | +from schooltool.course.interfaces import ISection |
1712 | +from schooltool.requirement.scoresystem import UNSCORED |
1713 | +from schooltool.skin import flourish |
1714 | + |
1715 | +from schooltool.virginia import VirginiaMessage as _ |
1716 | + |
1717 | + |
1718 | +class SectionReportViewBase(flourish.page.Page): |
1719 | + |
1720 | + passing_score_filter_id = 'passing-score-filter' |
1721 | + |
1722 | + @property |
1723 | + def subtitle(self): |
1724 | + return self.section.title |
1725 | + |
1726 | + @Lazy |
1727 | + def gradebook(self): |
1728 | + return removeSecurityProxy(self.context) |
1729 | + |
1730 | + @Lazy |
1731 | + def section(self): |
1732 | + return ISection(self.gradebook) |
1733 | + |
1734 | + @Lazy |
1735 | + def scoresystem(self): |
1736 | + return querySkillScoreSystem() |
1737 | + |
1738 | + @Lazy |
1739 | + def default_passing_score(self): |
1740 | + scores = self.scoresystem.scoresDict() |
1741 | + return scores[self.scoresystem._minPassingScore] |
1742 | + |
1743 | + @Lazy |
1744 | + def passing_scores(self): |
1745 | + result = [] |
1746 | + for score in self.scoresystem.scores: |
1747 | + label, abbr, value, percent = score |
1748 | + result.append({ |
1749 | + 'value': value, |
1750 | + 'submit_value': label, |
1751 | + 'label': abbr, |
1752 | + 'default': value == self.default_passing_score, |
1753 | + }) |
1754 | + return result |
1755 | + |
1756 | + @property |
1757 | + def passing_score(self): |
1758 | + passing_score_filter_id = self.passing_score_filter_id |
1759 | + submitted_passing_score = self.request.get(passing_score_filter_id) |
1760 | + if submitted_passing_score is not None: |
1761 | + for score in self.passing_scores: |
1762 | + if submitted_passing_score == score['submit_value']: |
1763 | + return score['value'] |
1764 | + return self.default_passing_score |
1765 | + |
1766 | + def render(self, *args, **kw): |
1767 | + zc.resourcelibrary.need('schooltool.virginia.flourish') |
1768 | + return super(SectionReportViewBase, self).render(*args, **kw) |
1769 | + |
1770 | + |
1771 | +class SectionReportDetailsViewletBase(flourish.viewlet.Viewlet): |
1772 | + |
1773 | + @property |
1774 | + def course(self): |
1775 | + course_titles = [] |
1776 | + course_government_ids = [] |
1777 | + section = self.view.section |
1778 | + for course in section.courses: |
1779 | + course_titles.append(course.title) |
1780 | + course_government_ids.append(course.government_id or '') |
1781 | + return { |
1782 | + 'title': ', '.join(course_titles), |
1783 | + 'government_id': ', '.join(course_government_ids), |
1784 | + } |
1785 | + |
1786 | + @property |
1787 | + def instructors(self): |
1788 | + instructors = [] |
1789 | + section = self.view.section |
1790 | + for person in section.instructors: |
1791 | + instructors.append(person.title) |
1792 | + return '<br />'.join(instructors) |
1793 | + |
1794 | + |
1795 | +class VASectionReportTablesViewlet(flourish.viewlet.Viewlet): |
1796 | + |
1797 | + template = ViewPageTemplateFile('templates/va_section_report_tables.pt') |
1798 | + |
1799 | + @property |
1800 | + def passing_scores_row(self): |
1801 | + result = [] |
1802 | + for passing_score in self.view.passing_scores: |
1803 | + if passing_score['value'] >= self.view.passing_score: |
1804 | + result.append(passing_score) |
1805 | + return '/'.join(reversed([i['submit_value'] for i in result])) |
1806 | + |
1807 | + |
1808 | +class VASectionReportPassingScoreViewlet(flourish.viewlet.Viewlet): |
1809 | + |
1810 | + template = ViewPageTemplateFile( |
1811 | + 'templates/va_section_report_passing_score.pt') |
1812 | + |
1813 | + def options(self): |
1814 | + result = [] |
1815 | + for passing_score in self.view.passing_scores: |
1816 | + selected = passing_score['value'] == self.view.passing_score |
1817 | + result.append({ |
1818 | + 'value': passing_score['submit_value'], |
1819 | + 'title': passing_score['label'], |
1820 | + 'selected': selected, |
1821 | + }) |
1822 | + return result |
1823 | + |
1824 | + |
1825 | +class VASectionReportView(SectionReportViewBase): |
1826 | + |
1827 | + container_class = 'container extra-wide-container' |
1828 | + |
1829 | + def update(self): |
1830 | + rows = [] |
1831 | + collator = ICollator(self.request.locale) |
1832 | + worksheets = self.context.__parent__.__parent__ |
1833 | + students_count = len(self.section.members) |
1834 | + for student in sorted(self.section.members, |
1835 | + key=lambda x:x.title, |
1836 | + cmp=collator.cmp): |
1837 | + demographics = IDemographics(student) |
1838 | + info = { |
1839 | + 'student': student.title, |
1840 | + 'student_id': student.username, |
1841 | + 'required_container_id': 'required-%s' % student.username, |
1842 | + 'all_container_id': 'all-%s' % student.username, |
1843 | + 'student_id': demographics.get('ID', ''), |
1844 | + } |
1845 | + info.update(self.getStudentSkillsData(student, worksheets)) |
1846 | + rows.append(info) |
1847 | + self.rows = rows |
1848 | + attainment_labels = {100: '90-100%', |
1849 | + 90: '80-89%', |
1850 | + 80: '70-79%', |
1851 | + 70: '60-69%', |
1852 | + 60: '<= 59%'} |
1853 | + rows_attainment = {100: 0, |
1854 | + 90: 0, |
1855 | + 80: 0, |
1856 | + 70: 0, |
1857 | + 60: 0} |
1858 | + rows_attainment80 = {u'80% or above': 0, |
1859 | + u'Below 80%': 0} |
1860 | + for row in self.rows: |
1861 | + rows_attainment[row['rows_attainment']] += 1 |
1862 | + rows_attainment80[row['rows_attainment80']] += 1 |
1863 | + self.rows_attainment = [] |
1864 | + for key in sorted(rows_attainment, reverse=True): |
1865 | + info = {"scale": attainment_labels[key], |
1866 | + "student_count": rows_attainment[key]} |
1867 | + try: |
1868 | + p = rows_attainment[key] / float(students_count) |
1869 | + except (ZeroDivisionError,): |
1870 | + p = 0 |
1871 | + info["percentage"] = self.formatPercentage(p) |
1872 | + self.rows_attainment.append(info) |
1873 | + self.rows_attainment80 = [] |
1874 | + for key in sorted(rows_attainment80): |
1875 | + info = {"scale": key, |
1876 | + "student_count": rows_attainment80[key]} |
1877 | + try: |
1878 | + p = rows_attainment80[key] / float(students_count) |
1879 | + except (ZeroDivisionError,): |
1880 | + p = 0 |
1881 | + info["percentage"] = self.formatPercentage(p) |
1882 | + self.rows_attainment80.append(info) |
1883 | + |
1884 | + def getStudentSkillsData(self, student, worksheets): |
1885 | + required_total = 0 |
1886 | + required_evaluated = 0 |
1887 | + required_competent = 0 |
1888 | + all_total = 0 |
1889 | + all_evaluated = 0 |
1890 | + all_competent = 0 |
1891 | + for worksheet in worksheets.values(): |
1892 | + gradebook = ISkillsGradebook(worksheet) |
1893 | + for skill in gradebook.activities: |
1894 | + all_total += 1 |
1895 | + score = gradebook.getScore(student, skill) |
1896 | + evaluated = score is not None and score.value is not UNSCORED |
1897 | + # XXX: use real scoresystem values here instead of int() |
1898 | + competent = evaluated and \ |
1899 | + int(score.value) >= int(self.passing_score) |
1900 | + if evaluated: |
1901 | + all_evaluated += 1 |
1902 | + if competent: |
1903 | + all_competent += 1 |
1904 | + if skill.required: |
1905 | + required_total += 1 |
1906 | + if evaluated: |
1907 | + required_evaluated += 1 |
1908 | + if competent: |
1909 | + required_competent += 1 |
1910 | + try: |
1911 | + required_rating = required_competent / float(required_evaluated) |
1912 | + except (ZeroDivisionError,): |
1913 | + required_rating = 0 |
1914 | + try: |
1915 | + required_completed = required_competent / float(required_total) |
1916 | + except (ZeroDivisionError,): |
1917 | + required_completed = 0 |
1918 | + try: |
1919 | + all_rating = all_competent / float(all_evaluated) |
1920 | + except (ZeroDivisionError,): |
1921 | + all_rating = 0 |
1922 | + required_completed_percentage = required_completed * 100 |
1923 | + if required_completed_percentage >= 90: |
1924 | + rows_attainment = 100 |
1925 | + elif required_completed_percentage >= 80: |
1926 | + rows_attainment = 90 |
1927 | + elif required_completed_percentage >= 70: |
1928 | + rows_attainment = 80 |
1929 | + elif required_completed_percentage >= 60: |
1930 | + rows_attainment = 70 |
1931 | + else: |
1932 | + rows_attainment = 60 |
1933 | + if required_completed_percentage >= 80: |
1934 | + rows_attainment80 = u'80% or above' |
1935 | + else: |
1936 | + rows_attainment80 = u'Below 80%' |
1937 | + return { |
1938 | + 'required_total': required_total, |
1939 | + 'required_evaluated': required_evaluated, |
1940 | + 'required_competent': required_competent, |
1941 | + 'required_rating': self.formatPercentage(required_rating), |
1942 | + 'required_completed': self.formatPercentage(required_completed), |
1943 | + 'required_data': [required_total, required_evaluated, required_competent], |
1944 | + 'all_total': all_total, |
1945 | + 'all_evaluated': all_evaluated, |
1946 | + 'all_competent': all_competent, |
1947 | + 'all_rating': self.formatPercentage(all_rating), |
1948 | + 'all_data': [all_total, all_evaluated, all_competent], |
1949 | + 'rows_attainment': rows_attainment, |
1950 | + 'rows_attainment80': rows_attainment80, |
1951 | + } |
1952 | + |
1953 | + def formatPercentage(self, number): |
1954 | + return '%.0f%%' % (number * 100) |
1955 | + |
1956 | + |
1957 | +class VASectionChartReportView(VASectionReportView): |
1958 | + |
1959 | + content_template = ViewPageTemplateFile( |
1960 | + 'templates/va_section_report_chart.pt') |
1961 | + |
1962 | + |
1963 | +class SectionReportView(SectionReportViewBase): |
1964 | + |
1965 | + form_container_id = 'section-report-form-container' |
1966 | + passing_target_filter_id = 'passing-target-filter' |
1967 | + skill_type_filter_id = 'skill-type-filter' |
1968 | + score_colors_container_id = 'score-colors-container' |
1969 | + default_skill_type = 'all' |
1970 | + default_passing_target = 80 |
1971 | + colors = {'passing': {'start': '#6B3A89', 'end': '#D1C4DA'}, |
1972 | + 'not_passing': {'start': '#F68E4D', 'end': '#FCD6C5'}} |
1973 | + |
1974 | + @Lazy |
1975 | + def skills(self): |
1976 | + result = [] |
1977 | + for worksheet in ISectionSkills(self.section).values(): |
1978 | + gradebook = ISkillsGradebook(worksheet) |
1979 | + for skill in worksheet.values(): |
1980 | + result.append((skill, gradebook)) |
1981 | + return self.filter_skills(result) |
1982 | + |
1983 | + def filter_skills(self, skills): |
1984 | + if self.skill_type == 'required': |
1985 | + condition = lambda (skill, gradebook): skill.required |
1986 | + else: |
1987 | + condition = None |
1988 | + return filter(condition, skills) |
1989 | + |
1990 | + @Lazy |
1991 | + def passing_targets(self): |
1992 | + result = [] |
1993 | + start = 10 |
1994 | + end = 100 |
1995 | + step = 10 |
1996 | + for value in range(start, end, step): |
1997 | + result.append({ |
1998 | + 'value': value, |
1999 | + 'submit_value': str(value), |
2000 | + 'default': value == self.default_passing_target, |
2001 | + }) |
2002 | + return result |
2003 | + |
2004 | + @property |
2005 | + def passing_target(self): |
2006 | + passing_target_filter_id = self.passing_target_filter_id |
2007 | + submitted_passing_target = self.request.get(passing_target_filter_id) |
2008 | + if submitted_passing_target is not None: |
2009 | + for target in self.passing_targets: |
2010 | + if submitted_passing_target == target['submit_value']: |
2011 | + return target['value'] |
2012 | + return self.default_passing_target |
2013 | + |
2014 | + @Lazy |
2015 | + def skill_types(self): |
2016 | + result = [] |
2017 | + labels = [_('All'), _('Required'), _('Evaluated')] |
2018 | + for label in labels: |
2019 | + value = str(label).lower() |
2020 | + result.append({ |
2021 | + 'value': value, |
2022 | + 'submit_value': value, |
2023 | + 'label': label, |
2024 | + 'default': value == self.default_skill_type, |
2025 | + }) |
2026 | + return result |
2027 | + |
2028 | + @property |
2029 | + def skill_type(self): |
2030 | + skill_type_filter_id = self.skill_type_filter_id |
2031 | + submitted_skill_type = self.request.get(skill_type_filter_id) |
2032 | + if submitted_skill_type is not None: |
2033 | + for skill_type in self.skill_types: |
2034 | + if submitted_skill_type == skill_type['submit_value']: |
2035 | + return skill_type['value'] |
2036 | + return self.default_skill_type |
2037 | + |
2038 | + def studentSkillsData(self, student): |
2039 | + grouped_by_score = {} |
2040 | + count = 0 |
2041 | + evaluated = 0 |
2042 | + for skill, gradebook in self.skills: |
2043 | + count += 1 |
2044 | + score = gradebook.getScore(student, skill) |
2045 | + if score is not None and score.value is not UNSCORED: |
2046 | + evaluated += 1 |
2047 | + if score.value not in grouped_by_score: |
2048 | + grouped_by_score[score.value] = 0 |
2049 | + grouped_by_score[score.value] += 1 |
2050 | + return grouped_by_score, evaluated, count |
2051 | + |
2052 | + def getChartData(self, student): |
2053 | + result = { |
2054 | + 'student': student, |
2055 | + 'container_id': 'skills-data-%s' % student.username, |
2056 | + } |
2057 | + grouped_by_score, evaluated, total = self.studentSkillsData(student) |
2058 | + if self.skill_type == 'evaluated': |
2059 | + skills_count = evaluated |
2060 | + else: |
2061 | + skills_count = total |
2062 | + result['skills_count'] = skills_count |
2063 | + result['chart_scores'] = self.getChartScores(grouped_by_score, |
2064 | + skills_count) |
2065 | + return result |
2066 | + |
2067 | + def getChartScores(self, student_data, skills_count): |
2068 | + passing = [] |
2069 | + not_passing = [] |
2070 | + for passing_score in reversed(self.passing_scores): |
2071 | + is_passing = passing_score['value'] >= self.passing_score |
2072 | + if is_passing: |
2073 | + score_list = passing |
2074 | + else: |
2075 | + score_list = not_passing |
2076 | + count = student_data.get(passing_score['submit_value'], 0) |
2077 | + score_list.append({ |
2078 | + 'count': count, |
2079 | + 'is_passing': is_passing, |
2080 | + 'label': passing_score['label'], |
2081 | + 'submit_value': passing_score['submit_value'], |
2082 | + 'value': float(passing_score['value']), |
2083 | + }) |
2084 | + not_passing_length = len(not_passing) |
2085 | + passing_length = len(passing) |
2086 | + for index, score_info in enumerate(not_passing): |
2087 | + x = sum([info['count'] for info in not_passing[index:]]) |
2088 | + try: |
2089 | + count = student_data.get(score_info['submit_value'], 0) |
2090 | + percentage = (count * 100.0) / skills_count |
2091 | + except (ZeroDivisionError,): |
2092 | + percentage = 0 |
2093 | + if not_passing_length > 1: |
2094 | + color_weight = float(index) / (not_passing_length - 1) |
2095 | + else: |
2096 | + color_weight = float(index) / not_passing_length |
2097 | + score_info.update({ |
2098 | + 'x': -x, |
2099 | + 'color_weight': color_weight, |
2100 | + 'above_passing_target': False, |
2101 | + 'percentage': '%.1f%%' % percentage, |
2102 | + }) |
2103 | + for index, score_info in enumerate(passing): |
2104 | + x = sum([info['count'] for info in passing[:index+1]]) |
2105 | + try: |
2106 | + count = student_data.get(score_info['submit_value'], 0) |
2107 | + percentage = (count * 100.0) / skills_count |
2108 | + except (ZeroDivisionError,): |
2109 | + percentage = 0 |
2110 | + try: |
2111 | + above_passing_target = (x * 100.0 / skills_count) >= self.passing_target |
2112 | + except (ZeroDivisionError,): |
2113 | + above_passing_target = False |
2114 | + if passing_length > 1: |
2115 | + color_weight = 1 - (float(index) / (passing_length - 1)) |
2116 | + else: |
2117 | + color_weight = 1 - (float(index) / passing_length) |
2118 | + score_info.update({ |
2119 | + 'x': x, |
2120 | + 'color_weight': color_weight, |
2121 | + 'above_passing_target': above_passing_target, |
2122 | + 'percentage': '%.1f%%' % percentage, |
2123 | + }) |
2124 | + passing.reverse() |
2125 | + result = not_passing + passing |
2126 | + return result |
2127 | + |
2128 | + |
2129 | +class SectionReportByStudentView(SectionReportView): |
2130 | + |
2131 | + pass |
2132 | + |
2133 | + |
2134 | +class SectionReportBySkillView(SectionReportView): |
2135 | + |
2136 | + container_class = 'container widecontainer' |
2137 | + |
2138 | + |
2139 | +class VAReportDetailsViewlet(SectionReportDetailsViewletBase): |
2140 | + |
2141 | + template = ViewPageTemplateFile( |
2142 | + 'templates/va_section_report_details.pt') |
2143 | + |
2144 | + |
2145 | +class ReportDetailsViewlet(SectionReportDetailsViewletBase): |
2146 | + |
2147 | + template = ViewPageTemplateFile( |
2148 | + 'templates/section_report_details.pt') |
2149 | + |
2150 | + |
2151 | +class SectionReportByStudentDescriptionViewlet(flourish.page.Related): |
2152 | + |
2153 | + template = InlineViewPageTemplate(''' |
2154 | + <tal:block i18n:domain="schooltool.virginia"> |
2155 | + <div class="header" i18n:translate=""> |
2156 | + Note: |
2157 | + </div> |
2158 | + <div class="body"> |
2159 | + <p i18n:domain="" i18n:translate=""> |
2160 | + Students are expected to achieve a satisfactory rating |
2161 | + (one of the three highest marks) on the Student Competency |
2162 | + Record (SCR) rating scale on at least 80% of the required |
2163 | + (essential) competencies. |
2164 | + </p> |
2165 | + </div> |
2166 | + </tal:block> |
2167 | + ''') |
2168 | + |
2169 | + |
2170 | +class SectionReportBySkillDescriptionViewlet(flourish.viewlet.Viewlet): |
2171 | + |
2172 | + template = InlineViewPageTemplate(''' |
2173 | + <p i18n:domain="" i18n:translate=""> |
2174 | + XXX This report description goes here XXX |
2175 | + </p> |
2176 | + ''') |
2177 | + |
2178 | + |
2179 | +class SectionReportTableViewlet(flourish.viewlet.Viewlet): |
2180 | + |
2181 | + template = InlineViewPageTemplate(''' |
2182 | + <div tal:content="structure view/view/providers/ajax/view/section/charts_table"/> |
2183 | + ''') |
2184 | + |
2185 | + |
2186 | +def student_id_getter(item, formatter): |
2187 | + student = item['student'] |
2188 | + demographics = IDemographics(student) |
2189 | + return demographics.get('ID', '') |
2190 | + |
2191 | + |
2192 | +class SectionReportChartsColumn(GetterColumn): |
2193 | + |
2194 | + template = ViewPageTemplateFile( |
2195 | + 'templates/section_report_charts_column.pt') |
2196 | + |
2197 | + |
2198 | +class SkillsColumn(SectionReportChartsColumn): |
2199 | + |
2200 | + svg = {'width': 220, 'height': 12} |
2201 | + |
2202 | + @Lazy |
2203 | + def chart(self): |
2204 | + margins = {'top': 0, 'bottom': 0, 'left': 10, 'right': 10} |
2205 | + w = self.svg['width'] - (margins['left'] + margins['right']) |
2206 | + h = self.svg['height'] - (margins['top'] + margins['bottom']) |
2207 | + return {'width': w, 'height': h, 'margins': margins} |
2208 | + |
2209 | + @property |
2210 | + def passing_target_size(self): |
2211 | + w = self.chart['width'] * ((self.passing_target/200.0)+0.5) |
2212 | + return {'width': w, 'height': self.svg['height']} |
2213 | + |
2214 | + def __init__(self, passing_target, colors): |
2215 | + self.passing_target = passing_target |
2216 | + self.colors = colors |
2217 | + title = _('% of Skills Below and Above Passing') |
2218 | + name = 'skills' |
2219 | + super(SkillsColumn, self).__init__(title, name=name) |
2220 | + |
2221 | + def renderCell(self, item, formatter): |
2222 | + container_id = item['container_id'] |
2223 | + skills_count = item['skills_count'] |
2224 | + chart_scores = item['chart_scores'] |
2225 | + # XXX: hack to make the ViewPageTemplateFile to work |
2226 | + self.context = item |
2227 | + self.request = formatter.request |
2228 | + return self.template(container_id=container_id, |
2229 | + svg_size=self.svg, |
2230 | + container_size=self.chart, |
2231 | + container_margins=self.chart['margins'], |
2232 | + skills_count=skills_count, |
2233 | + passing_target_size=self.passing_target_size, |
2234 | + scores=chart_scores, |
2235 | + colors=self.colors) |
2236 | + |
2237 | + |
2238 | +class StudentsColumn(SectionReportChartsColumn): |
2239 | + |
2240 | + def __init__(self, view): |
2241 | + self.view = view |
2242 | + self.total = len(view.section.members) |
2243 | + title = _('Students (${count})', |
2244 | + mapping={'count': self.total}) |
2245 | + name = 'students' |
2246 | + super(StudentsColumn, self).__init__(title, name=name) |
2247 | + |
2248 | + def renderCell(self, item, formatter): |
2249 | + skill, gradebook = item |
2250 | + container_id = 'skills-data-%s' % skill.__name__ |
2251 | + skill_data = self.skillScoresData(skill, gradebook) |
2252 | + data = [] |
2253 | + passing_index = None |
2254 | + scores = reversed(self.view.scoresystem.scores) |
2255 | + score_labels = [] |
2256 | + for i, score in enumerate(scores): |
2257 | + score_labels.append(score[1]) |
2258 | + data.append(skill_data[score[0]]) |
2259 | + if score[2] == self.view.passing_score: |
2260 | + passing_index = i |
2261 | + # XXX: hack to make the ViewPageTemplateFile to work |
2262 | + self.context = item |
2263 | + self.request = formatter.request |
2264 | + return self.template(total=self.total, |
2265 | + passing_index=passing_index, |
2266 | + data=data, |
2267 | + container_id=container_id, |
2268 | + score_labels = score_labels, |
2269 | + passing_target=self.view.passing_target) |
2270 | + |
2271 | + def skillScoresData(self, skill, gradebook): |
2272 | + result = {} |
2273 | + for score in self.view.scoresystem.scores: |
2274 | + result[score[0]] = 0 |
2275 | + for student in self.view.section.members: |
2276 | + score = gradebook.getScore(student, skill) |
2277 | + if score is not None and score.value is not UNSCORED: |
2278 | + result[score.value] += 1 |
2279 | + return result |
2280 | + |
2281 | + |
2282 | +class SectionReportSortFormatter(table.ajax.AJAXFormSortFormatter): |
2283 | + |
2284 | + script_name = 'ST.virginia.on_section_report_sort' |
2285 | + |
2286 | + def renderHeaders(self): |
2287 | + result = [] |
2288 | + old_css_class = self.cssClasses.get('th') |
2289 | + for col in self.visible_columns: |
2290 | + self.cssClasses['th'] = col.name.replace('_', '-') |
2291 | + result.append(self.renderHeader(col)) |
2292 | + self.cssClasses['th'] = old_css_class |
2293 | + return ''.join(result) |
2294 | + |
2295 | + |
2296 | +class SectionReportChartsTable(table.ajax.Table): |
2297 | + |
2298 | + batch_size = 0 |
2299 | + table_formatter = SectionReportSortFormatter |
2300 | + css_classes = '' |
2301 | + |
2302 | + @property |
2303 | + def html_id(self): |
2304 | + return self.view.form_container_id |
2305 | + |
2306 | + def updateFormatter(self): |
2307 | + if self._table_formatter is None: |
2308 | + self.setUp(table_formatter=self.table_formatter, |
2309 | + batch_size=self.batch_size, |
2310 | + prefix=self.__name__, |
2311 | + css_classes={'table': self.css_classes}) |
2312 | + |
2313 | + |
2314 | +class ByStudentChartsTable(SectionReportChartsTable): |
2315 | + |
2316 | + css_classes = 'section-report section-report-by-student' |
2317 | + |
2318 | + def columns(self): |
2319 | + title = table.column.LocaleAwareGetterColumn( |
2320 | + name='title', |
2321 | + title=_(u'Student'), |
2322 | + getter=lambda i, f: i['student'].title, |
2323 | + subsort=True) |
2324 | + ID = table.column.LocaleAwareGetterColumn( |
2325 | + name='ID', |
2326 | + title=_(u'Student ID #'), |
2327 | + getter=student_id_getter, |
2328 | + subsort=True) |
2329 | + skills = SkillsColumn(self.view.passing_target, self.view.colors) |
2330 | + return [title, ID, skills] |
2331 | + |
2332 | + def sortOn(self): |
2333 | + return (('title', False),) |
2334 | + |
2335 | + def items(self): |
2336 | + result = [] |
2337 | + for student in self.context.members: |
2338 | + result.append(self.view.getChartData(student)) |
2339 | + return result |
2340 | + |
2341 | + |
2342 | +class BySkillChartsTable(SectionReportChartsTable): |
2343 | + |
2344 | + css_classes = 'section-report section-report-by-skill' |
2345 | + |
2346 | + def columns(self): |
2347 | + # i => (skill, gradebook) |
2348 | + label = table.column.LocaleAwareGetterColumn( |
2349 | + name='label', |
2350 | + title=_(u'Label'), |
2351 | + getter=lambda i, f: i[0].label, |
2352 | + subsort=True) |
2353 | + title = table.column.LocaleAwareGetterColumn( |
2354 | + name='title', |
2355 | + title=_(u'Skill'), |
2356 | + getter=lambda i, f: i[0].title, |
2357 | + subsort=True) |
2358 | + students = StudentsColumn(self.view) |
2359 | + return [label, title, students] |
2360 | + |
2361 | + def sortOn(self): |
2362 | + return (('label', False),) |
2363 | + |
2364 | + def items(self): |
2365 | + return self.view.skills |
2366 | + |
2367 | + |
2368 | +class PassingScoreFilterViewlet(flourish.page.RefineLinksViewlet): pass |
2369 | + |
2370 | + |
2371 | +class PassingScoreFilterMenuViewlet(flourish.viewlet.Viewlet): |
2372 | + |
2373 | + template = InlineViewPageTemplate(''' |
2374 | + <select tal:attributes="id view/view/passing_score_filter_id; |
2375 | + name view/view/passing_score_filter_id;" |
2376 | + class="select-widget report-filter"> |
2377 | + <option tal:repeat="option view/options" |
2378 | + tal:content="option/title" |
2379 | + tal:attributes="value option/value; |
2380 | + selected option/selected;" |
2381 | + /> |
2382 | + </select> |
2383 | + ''') |
2384 | + |
2385 | + def options(self): |
2386 | + result = [] |
2387 | + for passing_score in self.view.passing_scores: |
2388 | + selected = passing_score['value'] == self.view.passing_score |
2389 | + result.append({ |
2390 | + 'value': passing_score['submit_value'], |
2391 | + 'title': passing_score['label'], |
2392 | + 'selected': selected, |
2393 | + }) |
2394 | + return result |
2395 | + |
2396 | + |
2397 | +class SkillTypeFilterViewlet(flourish.page.RefineLinksViewlet): pass |
2398 | + |
2399 | + |
2400 | +class SkillTypeFilterOptionsViewlet(flourish.viewlet.Viewlet): |
2401 | + |
2402 | + template = InlineViewPageTemplate(''' |
2403 | + <ul class="filter"> |
2404 | + <li tal:repeat="option view/options"> |
2405 | + <input type="radio" class="report-filter" |
2406 | + tal:attributes="id option/id; |
2407 | + name view/view/skill_type_filter_id; |
2408 | + value option/value; |
2409 | + checked option/selected;" |
2410 | + /> |
2411 | + <label tal:attributes="for option/id" |
2412 | + tal:content="option/title" /> |
2413 | + </li> |
2414 | + </ul> |
2415 | + ''') |
2416 | + |
2417 | + def options(self): |
2418 | + result = [] |
2419 | + skill_type_filter_id = self.view.skill_type_filter_id |
2420 | + for skill_type in self.view.skill_types: |
2421 | + option_id = '%s.%s' % (skill_type['submit_value'], |
2422 | + skill_type_filter_id) |
2423 | + selected = skill_type['value'] == self.view.skill_type |
2424 | + result.append({ |
2425 | + 'id': option_id, |
2426 | + 'value': skill_type['submit_value'], |
2427 | + 'title': skill_type['label'], |
2428 | + 'selected': selected, |
2429 | + }) |
2430 | + return result |
2431 | + |
2432 | + |
2433 | +class PassingTargetFilterViewlet(flourish.page.RefineLinksViewlet): pass |
2434 | + |
2435 | + |
2436 | +class PassingTargetFilterMenuViewlet(flourish.viewlet.Viewlet): |
2437 | + |
2438 | + template = InlineViewPageTemplate(''' |
2439 | + <select tal:attributes="id view/view/passing_target_filter_id; |
2440 | + name view/view/passing_target_filter_id;" |
2441 | + class="select-widget report-filter"> |
2442 | + <option tal:repeat="option view/options" |
2443 | + tal:content="option/title" |
2444 | + tal:attributes="value option/value; |
2445 | + selected option/selected;" |
2446 | + /> |
2447 | + </select> |
2448 | + ''') |
2449 | + |
2450 | + def options(self): |
2451 | + result = [] |
2452 | + for passing_target in self.view.passing_targets: |
2453 | + selected = passing_target['value'] == self.view.passing_target |
2454 | + result.append({ |
2455 | + 'value': passing_target['submit_value'], |
2456 | + 'title': '%d%%' % passing_target['value'], |
2457 | + 'selected': selected, |
2458 | + }) |
2459 | + return result |
2460 | + |
2461 | + |
2462 | +class SectionReportDoneButtonViewlet(flourish.viewlet.Viewlet): |
2463 | + |
2464 | + template = InlineViewPageTemplate(''' |
2465 | + <h3 i18n:domain="schooltool"> |
2466 | + <a tal:attributes="href view/nextURL" i18n:translate=""> |
2467 | + Done |
2468 | + </a> |
2469 | + </h3> |
2470 | + ''') |
2471 | + |
2472 | + def nextURL(self): |
2473 | + return absoluteURL(self.context, self.request) |
2474 | + |
2475 | + |
2476 | +class ScoreColorsViewlet(flourish.page.Related): |
2477 | + |
2478 | + template = ViewPageTemplateFile( |
2479 | + 'templates/section_report_score_colors.pt') |
2480 | + |
2481 | + |
2482 | +class ScoreColorColumn(GetterColumn): |
2483 | + |
2484 | + template = ViewPageTemplateFile( |
2485 | + 'templates/section_report_score_colors_column.pt') |
2486 | + |
2487 | + def __init__(self, *args, **kw): |
2488 | + self.colors = kw.pop('colors') |
2489 | + super(ScoreColorColumn, self).__init__(*args, **kw) |
2490 | + |
2491 | + def renderCell(self, item, formatter): |
2492 | + container_id = 'score-color-%s' % item['submit_value'] |
2493 | + # XXX: hack to make the ViewPageTemplateFile to work |
2494 | + self.context = item |
2495 | + self.request = formatter.request |
2496 | + return self.template(container_id=container_id, |
2497 | + score=item, |
2498 | + colors=self.colors) |
2499 | + |
2500 | + |
2501 | +class ScoreColorsTable(table.ajax.Table): |
2502 | + |
2503 | + batch_size = 0 |
2504 | + |
2505 | + @property |
2506 | + def html_id(self): |
2507 | + return self.view.score_colors_container_id |
2508 | + |
2509 | + def columns(self): |
2510 | + color = ScoreColorColumn( |
2511 | + name='color', |
2512 | + title=_(u'Score Colors'), |
2513 | + colors=self.view.colors) |
2514 | + return [color] |
2515 | + |
2516 | + def items(self): |
2517 | + passing = [] |
2518 | + not_passing = [] |
2519 | + for passing_score in self.view.passing_scores: |
2520 | + is_passing = passing_score['value'] >= self.view.passing_score |
2521 | + if is_passing: |
2522 | + score_list = passing |
2523 | + else: |
2524 | + score_list = not_passing |
2525 | + score_list.append({ |
2526 | + 'submit_value': passing_score['submit_value'], |
2527 | + 'is_passing': is_passing, |
2528 | + 'label': passing_score['label'], |
2529 | + }) |
2530 | + not_passing_length = len(not_passing) |
2531 | + passing_length = len(passing) |
2532 | + for index, score in enumerate(passing): |
2533 | + if passing_length > 1: |
2534 | + score['color_weight'] = float(index) / (len(passing)-1) |
2535 | + else: |
2536 | + score['color_weight'] = float(index) / len(passing) |
2537 | + for index, score in enumerate(reversed(not_passing)): |
2538 | + if not_passing_length > 1: |
2539 | + score['color_weight'] = float(index) / (len(not_passing)-1) |
2540 | + else: |
2541 | + score['color_weight'] = float(index) / len(not_passing) |
2542 | + return passing + not_passing |
2543 | + |
2544 | + def sortOn(self): |
2545 | + return None |
2546 | + |
2547 | + def updateFormatter(self): |
2548 | + if self._table_formatter is None: |
2549 | + self.setUp(table_formatter=self.table_formatter, |
2550 | + batch_size=self.batch_size, |
2551 | + prefix=self.__name__, |
2552 | + css_classes={'table': 'score-colors-table'}) |
2553 | |
2554 | === added directory 'src/schooltool/virginia/browser/resources' |
2555 | === added file 'src/schooltool/virginia/browser/resources/d3.v2.min.js' |
2556 | --- src/schooltool/virginia/browser/resources/d3.v2.min.js 1970-01-01 00:00:00 +0000 |
2557 | +++ src/schooltool/virginia/browser/resources/d3.v2.min.js 2012-10-08 23:48:26 +0000 |
2558 | @@ -0,0 +1,4 @@ |
2559 | +(function(){function d(a,b){try{for(var c in b)Object.defineProperty(a.prototype,c,{value:b[c],enumerable:!1})}catch(d){a.prototype=b}}function f(a){var b=-1,c=a.length,d=[];while(++b<c)d.push(a[b]);return d}function g(a){return Array.prototype.slice.call(a)}function j(){}function m(a){return a}function n(){return this}function o(){return!0}function p(a){return typeof a=="function"?a:function(){return a}}function q(a,b,c){return function(){var d=c.apply(b,arguments);return arguments.length?a:d}}function r(a){return a!=null&&!isNaN(a)}function s(a){return a.length}function u(a){return a==null}function v(a){return a.replace(/^\s+|\s+$/g,"").replace(/\s+/g," ")}function w(a){var b=1;while(a*b%1)b*=10;return b}function z(){}function A(a){function d(){var c=b,d=-1,e=c.length,f;while(++d<e)(f=c[d].on)&&f.apply(this,arguments);return a}var b=[],c=new j;return d.on=function(d,e){var f=c.get(d),g;return arguments.length<2?f&&f.on:(f&&(f.on=null,b=b.slice(0,g=b.indexOf(f)).concat(b.slice(g+1)),c.remove(d)),e&&b.push(c.set(d,{on:e})),a)},d}function D(a,b){return b-(a?1+Math.floor(Math.log(a+Math.pow(10,1+Math.floor(Math.log(a)/Math.LN10)-b))/Math.LN10):1)}function E(a){return a+""}function F(a){var b=a.lastIndexOf("."),c=b>=0?a.substring(b):(b=a.length,""),d=[];while(b>0)d.push(a.substring(b-=3,b+3));return d.reverse().join(",")+c}function H(a,b){var c=Math.pow(10,Math.abs(8-b)*3);return{scale:b>8?function(a){return a/c}:function(a){return a*c},symbol:a}}function N(a){return function(b){return b<=0?0:b>=1?1:a(b)}}function O(a){return function(b){return 1-a(1-b)}}function P(a){return function(b){return.5*(b<.5?a(2*b):2-a(2-2*b))}}function Q(a){return a}function R(a){return function(b){return Math.pow(b,a)}}function S(a){return 1-Math.cos(a*Math.PI/2)}function T(a){return Math.pow(2,10*(a-1))}function U(a){return 1-Math.sqrt(1-a*a)}function V(a,b){var c;return arguments.length<2&&(b=.45),arguments.length<1?(a=1,c=b/4):c=b/(2*Math.PI)*Math.asin(1/a),function(d){return 1+a*Math.pow(2,10*-d)*Math.sin((d-c)*2*Math.PI/b)}}function W(a){return a||(a=1.70158),function(b){return b*b*((a+1)*b-a)}}function X(a){return a<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+.9375:7.5625*(a-=2.625/2.75)*a+.984375}function Y(){d3.event.stopPropagation(),d3.event.preventDefault()}function Z(){var a=d3.event,b;while(b=a.sourceEvent)a=b;return a}function $(a){var b=new z,c=0,d=arguments.length;while(++c<d)b[arguments[c]]=A(b);return b.of=function(c,d){return function(e){try{var f=e.sourceEvent=d3.event;e.target=a,d3.event=e,b[e.type].apply(c,d)}finally{d3.event=f}}},b}function _(a){var b=[a.a,a.b],c=[a.c,a.d],d=bb(b),e=ba(b,c),f=bb(bc(c,b,-e))||0;b[0]*c[1]<c[0]*b[1]&&(b[0]*=-1,b[1]*=-1,d*=-1,e*=-1),this.rotate=(d?Math.atan2(b[1],b[0]):Math.atan2(-c[0],c[1]))*bd,this.translate=[a.e,a.f],this.scale=[d,f],this.skew=f?Math.atan2(e,f)*bd:0}function ba(a,b){return a[0]*b[0]+a[1]*b[1]}function bb(a){var b=Math.sqrt(ba(a,a));return b&&(a[0]/=b,a[1]/=b),b}function bc(a,b,c){return a[0]+=c*b[0],a[1]+=c*b[1],a}function bh(a){return{getItem:function(b){return{type:a.getItem(b).type,angle:0,matrix:be}}}}function bj(a){return a=="transform"?d3.interpolateTransform:d3.interpolate}function bk(a,b){return b=b-(a=+a)?1/(b-a):0,function(c){return(c-a)*b}}function bl(a,b){return b=b-(a=+a)?1/(b-a):0,function(c){return Math.max(0,Math.min(1,(c-a)*b))}}function bm(a,b,c){return new bn(a,b,c)}function bn(a,b,c){this.r=a,this.g=b,this.b=c}function bo(a){return a<16?"0"+Math.max(0,a).toString(16):Math.min(255,a).toString(16)}function bp(a,b,c){var d=0,e=0,f=0,g,h,i;g=/([a-z]+)\((.*)\)/i.exec(a);if(g){h=g[2].split(",");switch(g[1]){case"hsl":return c(parseFloat(h[0]),parseFloat(h[1])/100,parseFloat(h[2])/100);case"rgb":return b(br(h[0]),br(h[1]),br(h[2]))}}return(i=bs.get(a))?b(i.r,i.g,i.b):(a!=null&&a.charAt(0)==="#"&&(a.length===4?(d=a.charAt(1),d+=d,e=a.charAt(2),e+=e,f=a.charAt(3),f+=f):a.length===7&&(d=a.substring(1,3),e=a.substring(3,5),f=a.substring(5,7)),d=parseInt(d,16),e=parseInt(e,16),f=parseInt(f,16)),b(d,e,f))}function bq(a,b,c){var d=Math.min(a/=255,b/=255,c/=255),e=Math.max(a,b,c),f=e-d,g,h,i=(e+d)/2;return f?(h=i<.5?f/(e+d):f/(2-e-d),a==e?g=(b-c)/f+(b<c?6:0):b==e?g=(c-a)/f+2:g=(a-b)/f+4,g*=60):h=g=0,bt(g,h,i)}function br(a){var b=parseFloat(a);return a.charAt(a.length-1)==="%"?Math.round(b*2.55):b}function bt(a,b,c){return new bu(a,b,c)}function bu(a,b,c){this.h=a,this.s=b,this.l=c}function bv(a,b,c){function f(a){return a>360?a-=360:a<0&&(a+=360),a<60?d+(e-d)*a/60:a<180?e:a<240?d+(e-d)*(240-a)/60:d}function g(a){return Math.round(f(a)*255)}var d,e;return a%=360,a<0&&(a+=360),b=b<0?0:b>1?1:b,c=c<0?0:c>1?1:c,e=c<=.5?c*(1+b):c+b-c*b,d=2*c-e,bm(g(a+120),g(a),g(a-120))}function bw(a){return i(a,bC),a}function bD(a){return function(){return bx(a,this)}}function bE(a){return function(){return by(a,this)}}function bF(a,b){function f(){if(b=this.classList)return b.add(a);var b=this.className,d=b.baseVal!=null,e=d?b.baseVal:b;c.lastIndex=0,c.test(e)||(e=v(e+" "+a),d?b.baseVal=e:this.className=e)}function g(){if(b=this.classList)return b.remove(a);var b=this.className,d=b.baseVal!=null,e=d?b.baseVal:b;e&&(e=v(e.replace(c," ")),d?b.baseVal=e:this.className=e)}function h(){(b.apply(this,arguments)?f:g).call(this)}var c=new RegExp("(^|\\s+)"+d3.requote(a)+"(\\s+|$)","g");if(arguments.length<2){var d=this.node();if(e=d.classList)return e.contains(a);var e=d.className;return c.lastIndex=0,c.test(e.baseVal!=null?e.baseVal:e)}return this.each(typeof b=="function"?h:b?f:g)}function bG(a){return{__data__:a}}function bH(a){return function(){return bB(this,a)}}function bI(a){return arguments.length||(a=d3.ascending),function(b,c){return a(b&&b.__data__,c&&c.__data__)}}function bJ(a,b){for(var c=0,d=a.length;c<d;c++)for(var e=a[c],f=0,g=e.length,h;f<g;f++)(h=e[f])&&b(h,f,c);return a}function bL(a){return i(a,bM),a}function bN(a,b,c){i(a,bR);var d=new j,e=d3.dispatch("start","end"),f=bZ;return a.id=b,a.time=c,a.tween=function(b,c){return arguments.length<2?d.get(b):(c==null?d.remove(b):d.set(b,c),a)},a.ease=function(b){return arguments.length?(f=typeof b=="function"?b:d3.ease.apply(d3,arguments),a):f},a.each=function(b,c){return arguments.length<2?b$.call(a,b):(e.on(b,c),a)},d3.timer(function(g){return bJ(a,function(a,h,i){function o(f){return m.active>b?q():(m.active=b,d.forEach(function(b,c){(c=c.call(a,n,h))&&j.push(c)}),e.start.call(a,n,h),p(f)||d3.timer(p,0,c),1)}function p(c){if(m.active!==b)return q();var d=(c-k)/l,g=f(d),i=j.length;while(i>0)j[--i].call(a,g);if(d>=1)return q(),bT=b,e.end.call(a,n,h),bT=0,1}function q(){return--m.count||delete a.__transition__,1}var j=[],k=a.delay,l=a.duration,m=(a=a.node).__transition__||(a.__transition__={active:0,count:0}),n=a.__data__;++m.count,k<=g?o(g):d3.timer(o,k,c)})},0,c),a}function bP(a,b,c){return c!=""&&bO}function bQ(a,b){function d(a,d,e){var f=b.call(this,a,d);return f==null?e!=""&&bO:e!=f&&c(e,f)}function e(a,d,e){return e!=b&&c(e,b)}var c=bj(a);return typeof b=="function"?d:b==null?bP:(b+="",e)}function b$(a){var b=bT,c=bZ,d=bX,e=bY;return bT=this.id,bZ=this.ease(),bJ(this,function(b,c,d){bX=b.delay,bY=b.duration,a.call(b=b.node,b.__data__,c,d)}),bT=b,bZ=c,bX=d,bY=e,this}function cc(){var a,b=Date.now(),c=b_;while(c)a=b-c.then,a>=c.delay&&(c.flush=c.callback(a)),c=c.next;var d=cd()-b;d>24?(isFinite(d)&&(clearTimeout(cb),cb=setTimeout(cc,d)),ca=0):(ca=1,ce(cc))}function cd(){var a=null,b=b_,c=Infinity;while(b)b.flush?b=a?a.next=b.next:b_=b.next:(c=Math.min(c,b.then+b.delay),b=(a=b).next);return c}function cg(a,b){var c=a.ownerSVGElement||a;if(c.createSVGPoint){var d=c.createSVGPoint();if(cf<0&&(window.scrollX||window.scrollY)){c=d3.select(document.body).append("svg").style("position","absolute").style("top",0).style("left",0);var e=c[0][0].getScreenCTM();cf=!e.f&&!e.e,c.remove()}return cf?(d.x=b.pageX,d.y=b.pageY):(d.x=b.clientX,d.y=b.clientY),d=d.matrixTransform(a.getScreenCTM().inverse()),[d.x,d.y]}var f=a.getBoundingClientRect();return[b.clientX-f.left-a.clientLeft,b.clientY-f.top-a.clientTop]}function ch(){}function ci(a){var b=a[0],c=a[a.length-1];return b<c?[b,c]:[c,b]}function cj(a){return a.rangeExtent?a.rangeExtent():ci(a.range())}function ck(a,b){var c=0,d=a.length-1,e=a[c],f=a[d],g;f<e&&(g=c,c=d,d=g,g=e,e=f,f=g);if(g=f-e)b=b(g),a[c]=b.floor(e),a[d]=b.ceil(f);return a}function cl(){return Math}function cm(a,b,c,d){function g(){var g=Math.min(a.length,b.length)>2?ct:cs,i=d?bl:bk;return e=g(a,b,i,c),f=g(b,a,i,d3.interpolate),h}function h(a){return e(a)}var e,f;return h.invert=function(a){return f(a)},h.domain=function(b){return arguments.length?(a=b.map(Number),g()):a},h.range=function(a){return arguments.length?(b=a,g()):b},h.rangeRound=function(a){return h.range(a).interpolate(d3.interpolateRound)},h.clamp=function(a){return arguments.length?(d=a,g()):d},h.interpolate=function(a){return arguments.length?(c=a,g()):c},h.ticks=function(b){return cq(a,b)},h.tickFormat=function(b){return cr(a,b)},h.nice=function(){return ck(a,co),g()},h.copy=function(){return cm(a,b,c,d)},g()}function cn(a,b){return d3.rebind(a,b,"range","rangeRound","interpolate","clamp")}function co(a){return a=Math.pow(10,Math.round(Math.log(a)/Math.LN10)-1),{floor:function(b){return Math.floor(b/a)*a},ceil:function(b){return Math.ceil(b/a)*a}}}function cp(a,b){var c=ci(a),d=c[1]-c[0],e=Math.pow(10,Math.floor(Math.log(d/b)/Math.LN10)),f=b/d*e;return f<=.15?e*=10:f<=.35?e*=5:f<=.75&&(e*=2),c[0]=Math.ceil(c[0]/e)*e,c[1]=Math.floor(c[1]/e)*e+e*.5,c[2]=e,c}function cq(a,b){return d3.range.apply(d3,cp(a,b))}function cr(a,b){return d3.format(",."+Math.max(0,-Math.floor(Math.log(cp(a,b)[2])/Math.LN10+.01))+"f")}function cs(a,b,c,d){var e=c(a[0],a[1]),f=d(b[0],b[1]);return function(a){return f(e(a))}}function ct(a,b,c,d){var e=[],f=[],g=0,h=Math.min(a.length,b.length)-1;a[h]<a[0]&&(a=a.slice().reverse(),b=b.slice().reverse());while(++g<=h)e.push(c(a[g-1],a[g])),f.push(d(b[g-1],b[g]));return function(b){var c=d3.bisect(a,b,1,h)-1;return f[c](e[c](b))}}function cu(a,b){function d(c){return a(b(c))}var c=b.pow;return d.invert=function(b){return c(a.invert(b))},d.domain=function(e){return arguments.length?(b=e[0]<0?cx:cw,c=b.pow,a.domain(e.map(b)),d):a.domain().map(c)},d.nice=function(){return a.domain(ck(a.domain(),cl)),d},d.ticks=function(){var d=ci(a.domain()),e=[];if(d.every(isFinite)){var f=Math.floor(d[0]),g=Math.ceil(d[1]),h=c(d[0]),i=c(d[1]);if(b===cx){e.push(c(f));for(;f++<g;)for(var j=9;j>0;j--)e.push(c(f)*j)}else{for(;f<g;f++)for(var j=1;j<10;j++)e.push(c(f)*j);e.push(c(f))}for(f=0;e[f]<h;f++);for(g=e.length;e[g-1]>i;g--);e=e.slice(f,g)}return e},d.tickFormat=function(a,e){arguments.length<2&&(e=cv);if(arguments.length<1)return e;var f=Math.max(.1,a/d.ticks().length),g=b===cx?(h=-1e-12,Math.floor):(h=1e-12,Math.ceil),h;return function(a){return a/c(g(b(a)+h))<=f?e(a):""}},d.copy=function(){return cu(a.copy(),b)},cn(d,a)}function cw(a){return Math.log(a<0?0:a)/Math.LN10}function cx(a){return-Math.log(a>0?0:-a)/Math.LN10}function cy(a,b){function e(b){return a(c(b))}var c=cz(b),d=cz(1/b);return e.invert=function(b){return d(a.invert(b))},e.domain=function(b){return arguments.length?(a.domain(b.map(c)),e):a.domain().map(d)},e.ticks=function(a){return cq(e.domain(),a)},e.tickFormat=function(a){return cr(e.domain(),a)},e.nice=function(){return e.domain(ck(e.domain(),co))},e.exponent=function(a){if(!arguments.length)return b;var f=e.domain();return c=cz(b=a),d=cz(1/b),e.domain(f)},e.copy=function(){return cy(a.copy(),b)},cn(e,a)}function cz(a){return function(b){return b<0?-Math.pow(-b,a):Math.pow(b,a)}}function cA(a,b){function f(b){return d[((c.get(b)||c.set(b,a.push(b)))-1)%d.length]}function g(b,c){return d3.range(a.length).map(function(a){return b+c*a})}var c,d,e;return f.domain=function(d){if(!arguments.length)return a;a=[],c=new j;var e=-1,g=d.length,h;while(++e<g)c.has(h=d[e])||c.set(h,a.push(h));return f[b.t](b.x,b.p)},f.range=function(a){return arguments.length?(d=a,e=0,b={t:"range",x:a},f):d},f.rangePoints=function(c,h){arguments.length<2&&(h=0);var i=c[0],j=c[1],k=(j-i)/(a.length-1+h);return d=g(a.length<2?(i+j)/2:i+k*h/2,k),e=0,b={t:"rangePoints",x:c,p:h},f},f.rangeBands=function(c,h){arguments.length<2&&(h=0);var i=c[1]<c[0],j=c[i-0],k=c[1-i],l=(k-j)/(a.length+h);return d=g(j+l*h,l),i&&d.reverse(),e=l*(1-h),b={t:"rangeBands",x:c,p:h},f},f.rangeRoundBands=function(c,h){arguments.length<2&&(h=0);var i=c[1]<c[0],j=c[i-0],k=c[1-i],l=Math.floor((k-j)/(a.length+h)),m=k-j-(a.length-h)*l;return d=g(j+Math.round(m/2),l),i&&d.reverse(),e=Math.round(l*(1-h)),b={t:"rangeRoundBands",x:c,p:h},f},f.rangeBand=function(){return e},f.rangeExtent=function(){return ci(b.x)},f.copy=function(){return cA(a,b)},f.domain(a)}function cF(a,b){function d(){var d=0,f=a.length,g=b.length;c=[];while(++d<g)c[d-1]=d3.quantile(a,d/g);return e}function e(a){return isNaN(a=+a)?NaN:b[d3.bisect(c,a)]}var c;return e.domain=function(b){return arguments.length?(a=b.filter(function(a){return!isNaN(a)}).sort(d3.ascending),d()):a},e.range=function(a){return arguments.length?(b=a,d()):b},e.quantiles=function(){return c},e.copy=function(){return cF(a,b)},d()}function cG(a,b,c){function f(b){return c[Math.max(0,Math.min(e,Math.floor(d*(b-a))))]}function g(){return d=c.length/(b-a),e=c.length-1,f}var d,e;return f.domain=function(c){return arguments.length?(a=+c[0],b=+c[c.length-1],g()):[a,b]},f.range=function(a){return arguments.length?(c=a,g()):c},f.copy=function(){return cG(a,b,c)},g()}function cH(a){function b(a){return+a}return b.invert=b,b.domain=b.range=function(c){return arguments.length?(a=c.map(b),b):a},b.ticks=function(b){return cq(a,b)},b.tickFormat=function(b){return cr(a,b)},b.copy=function(){return cH(a)},b}function cK(a){return a.innerRadius}function cL(a){return a.outerRadius}function cM(a){return a.startAngle}function cN(a){return a.endAngle}function cO(a){function h(e){function o(){h.push("M",f(a(i),g))}var h=[],i=[],j=-1,k=e.length,l,m=p(b),n=p(c);while(++j<k)d.call(this,l=e[j],j)?i.push([+m.call(this,l,j),+n.call(this,l,j)]):i.length&&(o(),i=[]);return i.length&&o(),h.length?h.join(""):null}var b=cP,c=cQ,d=o,e=cR,f=cT,g=.7;return h.x=function(a){return arguments.length?(b=a,h):b},h.y=function(a){return arguments.length?(c=a,h):c},h.defined=function(a){return arguments.length?(d=a,h):d},h.interpolate=function(a){return arguments.length?(cS.has(a+="")||(a=cR),f=cS.get(e=a),h):e},h.tension=function(a){return arguments.length?(g=a,h):g},h}function cP(a){return a[0]}function cQ(a){return a[1]}function cT(a){return a.join("L")}function cU(a){return cT(a)+"Z"}function cV(a){var b=0,c=a.length,d=a[0],e=[d[0],",",d[1]];while(++b<c)e.push("V",(d=a[b])[1],"H",d[0]);return e.join("")}function cW(a){var b=0,c=a.length,d=a[0],e=[d[0],",",d[1]];while(++b<c)e.push("H",(d=a[b])[0],"V",d[1]);return e.join("")}function cX(a,b){return a.length<4?cT(a):a[1]+c$(a.slice(1,a.length-1),c_(a,b))}function cY(a,b){return a.length<3?cT(a):a[0]+c$((a.push(a[0]),a),c_([a[a.length-2]].concat(a,[a[1]]),b))}function cZ(a,b,c){return a.length<3?cT(a):a[0]+c$(a,c_(a,b))}function c$(a,b){if(b.length<1||a.length!=b.length&&a.length!=b.length+2)return cT(a);var c=a.length!=b.length,d="",e=a[0],f=a[1],g=b[0],h=g,i=1;c&&(d+="Q"+(f[0]-g[0]*2/3)+","+(f[1]-g[1]*2/3)+","+f[0]+","+f[1],e=a[1],i=2);if(b.length>1){h=b[1],f=a[i],i++,d+="C"+(e[0]+g[0])+","+(e[1]+g[1])+","+(f[0]-h[0])+","+(f[1]-h[1])+","+f[0]+","+f[1];for(var j=2;j<b.length;j++,i++)f=a[i],h=b[j],d+="S"+(f[0]-h[0])+","+(f[1]-h[1])+","+f[0]+","+f[1]}if(c){var k=a[i];d+="Q"+(f[0]+h[0]*2/3)+","+(f[1]+h[1]*2/3)+","+k[0]+","+k[1]}return d}function c_(a,b){var c=[],d=(1-b)/2,e,f=a[0],g=a[1],h=1,i=a.length;while(++h<i)e=f,f=g,g=a[h],c.push([d*(g[0]-e[0]),d*(g[1]-e[1])]);return c}function da(a){if(a.length<3)return cT(a);var b=1,c=a.length,d=a[0],e=d[0],f=d[1],g=[e,e,e,(d=a[1])[0]],h=[f,f,f,d[1]],i=[e,",",f];di(i,g,h);while(++b<c)d=a[b],g.shift(),g.push(d[0]),h.shift(),h.push(d[1]),di(i,g,h);b=-1;while(++b<2)g.shift(),g.push(d[0]),h.shift(),h.push(d[1]),di(i,g,h);return i.join("")}function db(a){if(a.length<4)return cT(a);var b=[],c=-1,d=a.length,e,f=[0],g=[0];while(++c<3)e=a[c],f.push(e[0]),g.push(e[1]);b.push(de(dh,f)+","+de(dh,g)),--c;while(++c<d)e=a[c],f.shift(),f.push(e[0]),g.shift(),g.push(e[1]),di(b,f,g);return b.join("")}function dc(a){var b,c=-1,d=a.length,e=d+4,f,g=[],h=[];while(++c<4)f=a[c%d],g.push(f[0]),h.push(f[1]);b=[de(dh,g),",",de(dh,h)],--c;while(++c<e)f=a[c%d],g.shift(),g.push(f[0]),h.shift(),h.push(f[1]),di(b,g,h);return b.join("")}function dd(a,b){var c=a.length-1;if(c){var d=a[0][0],e=a[0][1],f=a[c][0]-d,g=a[c][1]-e,h=-1,i,j;while(++h<=c)i=a[h],j=h/c,i[0]=b*i[0]+(1-b)*(d+j*f),i[1]=b*i[1]+(1-b)*(e+j*g)}return da(a)}function de(a,b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3]}function di(a,b,c){a.push("C",de(df,b),",",de(df,c),",",de(dg,b),",",de(dg,c),",",de(dh,b),",",de(dh,c))}function dj(a,b){return(b[1]-a[1])/(b[0]-a[0])}function dk(a){var b=0,c=a.length-1,d=[],e=a[0],f=a[1],g=d[0]=dj(e,f);while(++b<c)d[b]=g+(g=dj(e=f,f=a[b+1]));return d[b]=g,d}function dl(a){var b=[],c,d,e,f,g=dk(a),h=-1,i=a.length-1;while(++h<i)c=dj(a[h],a[h+1]),Math.abs(c)<1e-6?g[h]=g[h+1]=0:(d=g[h]/c,e=g[h+1]/c,f=d*d+e*e,f>9&&(f=c*3/Math.sqrt(f),g[h]=f*d,g[h+1]=f*e));h=-1;while(++h<=i)f=(a[Math.min(i,h+1)][0]-a[Math.max(0,h-1)][0])/(6*(1+g[h]*g[h])),b.push([f||0,g[h]*f||0]);return b}function dm(a){return a.length<3?cT(a):a[0]+c$(a,dl(a))}function dn(a){var b,c=-1,d=a.length,e,f;while(++c<d)b=a[c],e=b[0],f=b[1]+cI,b[0]=e*Math.cos(f),b[1]=e*Math.sin(f);return a}function dp(a){function l(g){function y(){l.push("M",h(a(n),k),j,i(a(m.reverse()),k),"Z")}var l=[],m=[],n=[],o=-1,q=g.length,r,s=p(b),t=p(d),u=b===c?function(){return w}:p(c),v=d===e?function(){return x}:p(e),w,x;while(++o<q)f.call(this,r=g[o],o)?(m.push([w=+s.call(this,r,o),x=+t.call(this,r,o)]),n.push([+u.call(this,r,o),+v.call(this,r,o)])):m.length&&(y(),m=[],n=[]);return m.length&&y(),l.length?l.join(""):null}var b=cP,c=cP,d=0,e=cQ,f=o,g=cR,h=cT,i=cT,j="L",k=.7;return l.x=function(a){return arguments.length?(b=c=a,l):c},l.x0=function(a){return arguments.length?(b=a,l):b},l.x1=function(a){return arguments.length?(c=a,l):c},l.y=function(a){return arguments.length?(d=e=a,l):e},l.y0=function(a){return arguments.length?(d=a,l):d},l.y1=function(a){return arguments.length?(e=a,l):e},l.defined=function(a){return arguments.length?(f=a,l):f},l.interpolate=function(a){return arguments.length?(cS.has(a+="")||(a=cR),h=cS.get(g=a),i=h.reverse||h,j=/-closed$/.test(a)?"M":"L",l):g},l.tension=function(a){return arguments.length?(k=a,l):k},l}function dq(a){return a.source}function dr(a){return a.target}function ds(a){return a.radius}function dt(a){return a.startAngle}function du(a){return a.endAngle}function dv(a){return[a.x,a.y]}function dw(a){return function(){var b=a.apply(this,arguments),c=b[0],d=b[1]+cI;return[c*Math.cos(d),c*Math.sin(d)]}}function dx(){return 64}function dy(){return"circle"}function dz(a){var b=Math.sqrt(a/Math.PI);return"M0,"+b+"A"+b+","+b+" 0 1,1 0,"+ -b+"A"+b+","+b+" 0 1,1 0,"+b+"Z"}function dD(a,b){a.attr("transform",function(a){return"translate("+b(a)+",0)"})}function dE(a,b){a.attr("transform",function(a){return"translate(0,"+b(a)+")"})}function dF(a,b,c){e=[];if(c&&b.length>1){var d=ci(a.domain()),e,f=-1,g=b.length,h=(b[1]-b[0])/++c,i,j;while(++f<g)for(i=c;--i>0;)(j=+b[f]-i*h)>=d[0]&&e.push(j);for(--f,i=0;++i<c&&(j=+b[f]+i*h)<d[1];)e.push(j)}return e}function dK(){dI||(dI=d3.select("body").append("div").style("visibility","hidden").style("top",0).style("height",0).style("width",0).style("overflow-y","scroll").append("div").style("height","2000px").node().parentNode);var a=d3.event,b;try{dI.scrollTop=1e3,dI.dispatchEvent(a),b=1e3-dI.scrollTop}catch(c){b=a.wheelDelta||-a.detail*5}return b}function dL(a){var b=a.source,c=a.target,d=dN(b,c),e=[b];while(b!==d)b=b.parent,e.push(b);var f=e.length;while(c!==d)e.splice(f,0,c),c=c.parent;return e}function dM(a){var b=[],c=a.parent;while(c!=null)b.push(a),a=c,c=c.parent;return b.push(a),b}function dN(a,b){if(a===b)return a;var c=dM(a),d=dM(b),e=c.pop(),f=d.pop(),g=null;while(e===f)g=e,e=c.pop(),f=d.pop();return g}function dQ(a){a.fixed|=2}function dR(a){a!==dP&&(a.fixed&=1)}function dS(){dP.fixed&=1,dO=dP=null}function dT(){dP.px=d3.event.x,dP.py=d3.event.y,dO.resume()}function dU(a,b,c){var d=0,e=0;a.charge=0;if(!a.leaf){var f=a.nodes,g=f.length,h=-1,i;while(++h<g){i=f[h];if(i==null)continue;dU(i,b,c),a.charge+=i.charge,d+=i.charge*i.cx,e+=i.charge*i.cy}}if(a.point){a.leaf||(a.point.x+=Math.random()-.5,a.point.y+=Math.random()-.5);var j=b*c[a.point.index];a.charge+=a.pointCharge=j,d+=j*a.point.x,e+=j*a.point.y}a.cx=d/a.charge,a.cy=e/a.charge}function dV(a){return 20}function dW(a){return 1}function dY(a){return a.x}function dZ(a){return a.y}function d$(a,b,c){a.y0=b,a.y=c}function eb(a){return d3.range(a.length)}function ec(a){var b=-1,c=a[0].length,d=[];while(++b<c)d[b]=0;return d}function ed(a){var b=1,c=0,d=a[0][1],e,f=a.length;for(;b<f;++b)(e=a[b][1])>d&&(c=b,d=e);return c}function ee(a){return a.reduce(ef,0)}function ef(a,b){return a+b[1]}function eg(a,b){return eh(a,Math.ceil(Math.log(b.length)/Math.LN2+1))}function eh(a,b){var c=-1,d=+a[0],e=(a[1]-d)/b,f=[];while(++c<=b)f[c]=e*c+d;return f}function ei(a){return[d3.min(a),d3.max(a)]}function ej(a,b){return d3.rebind(a,b,"sort","children","value"),a.links=en,a.nodes=function(b){return eo=!0,(a.nodes=a)(b)},a}function ek(a){return a.children}function el(a){return a.value}function em(a,b){return b.value-a.value}function en(a){return d3.merge(a.map(function(a){return(a.children||[]).map(function(b){return{source:a,target:b}})}))}function ep(a,b){return a.value-b.value}function eq(a,b){var c=a._pack_next;a._pack_next=b,b._pack_prev=a,b._pack_next=c,c._pack_prev=b}function er(a,b){a._pack_next=b,b._pack_prev=a}function es(a,b){var c=b.x-a.x,d=b.y-a.y,e=a.r+b.r;return e*e-c*c-d*d>.001}function et(a){function l(a){b=Math.min(a.x-a.r,b),c=Math.max(a.x+a.r,c),d=Math.min(a.y-a.r,d),e=Math.max(a.y+a.r,e)}var b=Infinity,c=-Infinity,d=Infinity,e=-Infinity,f=a.length,g,h,i,j,k;a.forEach(eu),g=a[0],g.x=-g.r,g.y=0,l(g);if(f>1){h=a[1],h.x=h.r,h.y=0,l(h);if(f>2){i=a[2],ey(g,h,i),l(i),eq(g,i),g._pack_prev=i,eq(i,h),h=g._pack_next;for(var m=3;m<f;m++){ey(g,h,i=a[m]);var n=0,o=1,p=1;for(j=h._pack_next;j!==h;j=j._pack_next,o++)if(es(j,i)){n=1;break}if(n==1)for(k=g._pack_prev;k!==j._pack_prev;k=k._pack_prev,p++)if(es(k,i))break;n?(o<p||o==p&&h.r<g.r?er(g,h=j):er(g=k,h),m--):(eq(g,i),h=i,l(i))}}}var q=(b+c)/2,r=(d+e)/2,s=0;for(var m=0;m<f;m++){var t=a[m];t.x-=q,t.y-=r,s=Math.max(s,t.r+Math.sqrt(t.x*t.x+t.y*t.y))}return a.forEach(ev),s}function eu(a){a._pack_next=a._pack_prev=a}function ev(a){delete a._pack_next,delete a._pack_prev}function ew(a){var b=a.children;b&&b.length?(b.forEach(ew),a.r=et(b)):a.r=Math.sqrt(a.value)}function ex(a,b,c,d){var e=a.children;a.x=b+=d*a.x,a.y=c+=d*a.y,a.r*=d;if(e){var f=-1,g=e.length;while(++f<g)ex(e[f],b,c,d)}}function ey(a,b,c){var d=a.r+c.r,e=b.x-a.x,f=b.y-a.y;if(d&&(e||f)){var g=b.r+c.r,h=e*e+f*f;g*=g,d*=d;var i=.5+(d-g)/(2*h),j=Math.sqrt(Math.max(0,2*g*(d+h)-(d-=h)*d-g*g))/(2*h);c.x=a.x+i*e+j*f,c.y=a.y+i*f-j*e}else c.x=a.x+d,c.y=a.y}function ez(a){return 1+d3.max(a,function(a){return a.y})}function eA(a){return a.reduce(function(a,b){return a+b.x},0)/a.length}function eB(a){var b=a.children;return b&&b.length?eB(b[0]):a}function eC(a){var b=a.children,c;return b&&(c=b.length)?eC(b[c-1]):a}function eD(a,b){return a.parent==b.parent?1:2}function eE(a){var b=a.children;return b&&b.length?b[0]:a._tree.thread}function eF(a){var b=a.children,c;return b&&(c=b.length)?b[c-1]:a._tree.thread}function eG(a,b){var c=a.children;if(c&&(e=c.length)){var d,e,f=-1;while(++f<e)b(d=eG(c[f],b),a)>0&&(a=d)}return a}function eH(a,b){return a.x-b.x}function eI(a,b){return b.x-a.x}function eJ(a,b){return a.depth-b.depth}function eK(a,b){function c(a,d){var e=a.children;if(e&&(i=e.length)){var f,g=null,h=-1,i;while(++h<i)f=e[h],c(f,g),g=f}b(a,d)}c(a,null)}function eL(a){var b=0,c=0,d=a.children,e=d.length,f;while(--e>=0)f=d[e]._tree,f.prelim+=b,f.mod+=b,b+=f.shift+(c+=f.change)}function eM(a,b,c){a=a._tree,b=b._tree;var d=c/(b.number-a.number);a.change+=d,b.change-=d,b.shift+=c,b.prelim+=c,b.mod+=c}function eN(a,b,c){return a._tree.ancestor.parent==b.parent?a._tree.ancestor:c}function eO(a){return{x:a.x,y:a.y,dx:a.dx,dy:a.dy}}function eP(a,b){var c=a.x+b[3],d=a.y+b[0],e=a.dx-b[1]-b[3],f=a.dy-b[0]-b[2];return e<0&&(c+=e/2,e=0),f<0&&(d+=f/2,f=0),{x:c,y:d,dx:e,dy:f}}function eQ(a){return a.map(eR).join(",")}function eR(a){return/[",\n]/.test(a)?'"'+a.replace(/\"/g,'""')+'"':a}function eT(a,b){return function(c){return c&&a.hasOwnProperty(c.type)?a[c.type](c):b}}function eU(a){return"m0,"+a+"a"+a+","+a+" 0 1,1 0,"+ -2*a+"a"+a+","+a+" 0 1,1 0,"+2*a+"z"}function eV(a,b){eW.hasOwnProperty(a.type)&&eW[a.type](a,b)}function eX(a,b){eV(a.geometry,b)}function eY(a,b){for(var c=a.features,d=0,e=c.length;d<e;d++)eV(c[d].geometry,b)}function eZ(a,b){for(var c=a.geometries,d=0,e=c.length;d<e;d++)eV(c[d],b)}function e$(a,b){for(var c=a.coordinates,d=0,e=c.length;d<e;d++)b.apply(null,c[d])}function e_(a,b){for(var c=a.coordinates,d=0,e=c.length;d<e;d++)for(var f=c[d],g=0,h=f.length;g<h;g++)b.apply(null,f[g])}function fa(a,b){for(var c=a.coordinates,d=0,e=c.length;d<e;d++)for(var f=c[d][0],g=0,h=f.length;g<h;g++)b.apply(null,f[g])}function fb(a,b){b.apply(null,a.coordinates)}function fc(a,b){for(var c=a.coordinates[0],d=0,e=c.length;d<e;d++)b.apply(null,c[d])}function fd(a){return a.source}function fe(a){return a.target}function ff(){function o(a){var b=Math.sin(a*=m)*n,c=Math.sin(m-a)*n,g=c*e+b*k,h=c*f+b*l,i=c*d+b*j;return[Math.atan2(h,g)/eS,Math.atan2(i,Math.sqrt(g*g+h*h))/eS]}var a,b,c,d,e,f,g,h,i,j,k,l,m,n;return o.distance=function(){return m==null&&(n=1/Math.sin(m=Math.acos(Math.max(-1,Math.min(1,d*j+c*i*Math.cos(g-a)))))),m},o.source=function(g){var h=Math.cos(a=g[0]*eS),i=Math.sin(a);return c=Math.cos(b=g[1]*eS),d=Math.sin(b),e=c*h,f=c*i,m=null,o},o.target=function(a){var b=Math.cos(g=a[0]*eS),c=Math.sin(g);return i=Math.cos(h=a[1]*eS),j=Math.sin(h),k=i*b,l=i*c,m=null,o},o}function fg(a,b){var c=ff().source(a).target(b);return c.distance(),c}function fj(a){var b=0,c=0;for(;;){if(a(b,c))return[b,c];b===0?(b=c+1,c=0):(b-=1,c+=1)}}function fk(a,b,c,d){var e,f,g,h,i,j,k;return e=d[a],f=e[0],g=e[1],e=d[b],h=e[0],i=e[1],e=d[c],j=e[0],k=e[1],(k-g)*(h-f)-(i-g)*(j-f)>0}function fl(a,b,c){return(c[0]-b[0])*(a[1]-b[1])<(c[1]-b[1])*(a[0]-b[0])}function fm(a,b,c,d){var e=a[0],f=b[0],g=c[0],h=d[0],i=a[1],j=b[1],k=c[1],l=d[1],m=e-g,n=f-e,o=h-g,p=i-k,q=j-i,r=l-k,s=(o*p-r*m)/(r*n-o*q);return[e+s*n,i+s*q]}function fo(a,b){var c={list:a.map(function(a,b){return{index:b,x:a[0],y:a[1]}}).sort(function(a,b){return a.y<b.y?-1:a.y>b.y?1:a.x<b.x?-1:a.x>b.x?1:0}),bottomSite:null},d={list:[],leftEnd:null,rightEnd:null,init:function(){d.leftEnd=d.createHalfEdge(null,"l"),d.rightEnd=d.createHalfEdge(null,"l"),d.leftEnd.r=d.rightEnd,d.rightEnd.l=d.leftEnd,d.list.unshift(d.leftEnd,d.rightEnd)},createHalfEdge:function(a,b){return{edge:a,side:b,vertex:null,l:null,r:null}},insert:function(a,b){b.l=a,b.r=a.r,a.r.l=b,a.r=b},leftBound:function(a){var b=d.leftEnd;do b=b.r;while(b!=d.rightEnd&&e.rightOf(b,a));return b=b.l,b},del:function(a){a.l.r=a.r,a.r.l=a.l,a.edge=null},right:function(a){return a.r},left:function(a){return a.l},leftRegion:function(a){return a.edge==null?c.bottomSite:a.edge.region[a.side]},rightRegion:function(a){return a.edge==null?c.bottomSite:a.edge.region[fn[a.side]]}},e={bisect:function(a,b){var c={region:{l:a,r:b},ep:{l:null,r:null}},d=b.x-a.x,e=b.y-a.y,f=d>0?d:-d,g=e>0?e:-e;return c.c=a.x*d+a.y*e+(d*d+e*e)*.5,f>g?(c.a=1,c.b=e/d,c.c/=d):(c.b=1,c.a=d/e,c.c/=e),c},intersect:function(a,b){var c=a.edge,d=b.edge;if(!c||!d||c.region.r==d.region.r)return null;var e=c.a*d.b-c.b*d.a;if(Math.abs(e)<1e-10)return null;var f=(c.c*d.b-d.c*c.b)/e,g=(d.c*c.a-c.c*d.a)/e,h=c.region.r,i=d.region.r,j,k;h.y<i.y||h.y==i.y&&h.x<i.x?(j=a,k=c):(j=b,k=d);var l=f>=k.region.r.x;return l&&j.side==="l"||!l&&j.side==="r"?null:{x:f,y:g}},rightOf:function(a,b){var c=a.edge,d=c.region.r,e=b.x>d.x;if(e&&a.side==="l")return 1;if(!e&&a.side==="r")return 0;if(c.a===1){var f=b.y-d.y,g=b.x-d.x,h=0,i=0;!e&&c.b<0||e&&c.b>=0?i=h=f>=c.b*g:(i=b.x+b.y*c.b>c.c,c.b<0&&(i=!i),i||(h=1));if(!h){var j=d.x-c.region.l.x;i=c.b*(g*g-f*f)<j*f*(1+2*g/j+c.b*c.b),c.b<0&&(i=!i)}}else{var k=c.c-c.a*b.x,l=b.y-k,m=b.x-d.x,n=k-d.y;i=l*l>m*m+n*n}return a.side==="l"?i:!i},endPoint:function(a,c,d){a.ep[c]=d;if(!a.ep[fn[c]])return;b(a)},distance:function(a,b){var c=a.x-b.x,d=a.y-b.y;return Math.sqrt(c*c+d*d)}},f={list:[],insert:function(a,b,c){a.vertex=b,a.ystar=b.y+c;for(var d=0,e=f.list,g=e.length;d<g;d++){var h=e[d];if(a.ystar>h.ystar||a.ystar==h.ystar&&b.x>h.vertex.x)continue;break}e.splice(d,0,a)},del:function(a){for(var b=0,c=f.list,d=c.length;b<d&&c[b]!=a;++b);c.splice(b,1)},empty:function(){return f.list.length===0},nextEvent:function(a){for(var b=0,c=f.list,d=c.length;b<d;++b)if(c[b]==a)return c[b+1];return null},min:function(){var a=f.list[0];return{x:a.vertex.x,y:a.ystar}},extractMin:function(){return f.list.shift()}};d.init(),c.bottomSite=c.list.shift();var g=c.list.shift(),h,i,j,k,l,m,n,o,p,q,r,s,t;for(;;){f.empty()||(h=f.min());if(g&&(f.empty()||g.y<h.y||g.y==h.y&&g.x<h.x))i=d.leftBound(g),j=d.right(i),n=d.rightRegion(i),s=e.bisect(n,g),m=d.createHalfEdge(s,"l"),d.insert(i,m),q=e.intersect(i,m),q&&(f.del(i),f.insert(i,q,e.distance(q,g))),i=m,m=d.createHalfEdge(s,"r"),d.insert(i,m),q=e.intersect(m,j),q&&f.insert(m,q,e.distance(q,g)),g=c.list.shift();else if(!f.empty())i=f.extractMin(),k=d.left(i),j=d.right(i),l=d.right(j),n=d.leftRegion(i),o=d.rightRegion(j),r=i.vertex,e.endPoint(i.edge,i.side,r),e.endPoint(j.edge,j.side,r),d.del(i),f.del(j),d.del(j),t="l",n.y>o.y&&(p=n,n=o,o=p,t="r"),s=e.bisect(n,o),m=d.createHalfEdge(s,t),d.insert(k,m),e.endPoint(s,fn[t],r),q=e.intersect(k,m),q&&(f.del(k),f.insert(k,q,e.distance(q,n))),q=e.intersect(m,l),q&&f.insert(m,q,e.distance(q,n));else break}for(i=d.right(d.leftEnd);i!=d.rightEnd;i=d.right(i))b(i.edge)}function fp(){return{leaf:!0,nodes:[],point:null}}function fq(a,b,c,d,e,f){if(!a(b,c,d,e,f)){var g=(c+e)*.5,h=(d+f)*.5,i=b.nodes;i[0]&&fq(a,i[0],c,d,g,h),i[1]&&fq(a,i[1],g,d,e,h),i[2]&&fq(a,i[2],c,h,g,f),i[3]&&fq(a,i[3],g,h,e,f)}}function fr(a){return{x:a[0],y:a[1]}}function ft(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function fv(a,b,c,d){var e,f,g=0,h=b.length,i=c.length;while(g<h){if(d>=i)return-1;e=b.charCodeAt(g++);if(e==37){f=fB[b.charAt(g++)];if(!f||(d=f(a,c,d))<0)return-1}else if(e!=c.charCodeAt(d++))return-1}return d}function fC(a,b,c){return fE.test(b.substring(c,c+=3))?c:-1}function fD(a,b,c){fF.lastIndex=0;var d=fF.exec(b.substring(c,c+10));return d?c+=d[0].length:-1}function fH(a,b,c){var d=fI.get(b.substring(c,c+=3).toLowerCase());return d==null?-1:(a.m=d,c)}function fJ(a,b,c){fK.lastIndex=0;var d=fK.exec(b.substring(c,c+12));return d?(a.m=fL.get(d[0].toLowerCase()),c+=d[0].length):-1}function fN(a,b,c){return fv(a,fA.c.toString(),b,c)}function fO(a,b,c){return fv(a,fA.x.toString(),b,c)}function fP(a,b,c){return fv(a,fA.X.toString(),b,c)}function fQ(a,b,c){fZ.lastIndex=0;var d=fZ.exec(b.substring(c,c+4));return d?(a.y=+d[0],c+=d[0].length):-1}function fR(a,b,c){fZ.lastIndex=0;var d=fZ.exec(b.substring(c,c+2));return d?(a.y=fS(+d[0]),c+=d[0].length):-1}function fS(a){return a+(a>68?1900:2e3)}function fT(a,b,c){fZ.lastIndex=0;var d=fZ.exec(b.substring(c,c+2));return d?(a.m=d[0]-1,c+=d[0].length):-1}function fU(a,b,c){fZ.lastIndex=0;var d=fZ.exec(b.substring(c,c+2));return d?(a.d=+d[0],c+=d[0].length):-1}function fV(a,b,c){fZ.lastIndex=0;var d=fZ.exec(b.substring(c,c+2));return d?(a.H=+d[0],c+=d[0].length):-1}function fW(a,b,c){fZ.lastIndex=0;var d=fZ.exec(b.substring(c,c+2));return d?(a.M=+d[0],c+=d[0].length):-1}function fX(a,b,c){fZ.lastIndex=0;var d=fZ.exec(b.substring(c,c+2));return d?(a.S=+d[0],c+=d[0].length):-1}function fY(a,b,c){fZ.lastIndex=0;var d=fZ.exec(b.substring(c,c+3));return d?(a.L=+d[0],c+=d[0].length):-1}function f$(a,b,c){var d=f_.get(b.substring(c,c+=2).toLowerCase());return d==null?-1:(a.p=d,c)}function ga(a){var b=a.getTimezoneOffset(),c=b>0?"-":"+",d=~~(Math.abs(b)/60),e=Math.abs(b)%60;return c+fw(d)+fw(e)}function gc(a){return a.toISOString()}function gd(a,b,c){function d(b){var c=a(b),d=f(c,1);return b-c<d-b?c:d}function e(c){return b(c=a(new fs(c-1)),1),c}function f(a,c){return b(a=new fs(+a),c),a}function g(a,d,f){var g=e(a),h=[];if(f>1)while(g<d)c(g)%f||h.push(new Date(+g)),b(g,1);else while(g<d)h.push(new Date(+g)),b(g,1);return h}function h(a,b |
2560 | +,c){try{fs=ft;var d=new ft;return d._=a,g(d,b,c)}finally{fs=Date}}a.floor=a,a.round=d,a.ceil=e,a.offset=f,a.range=g;var i=a.utc=ge(a);return i.floor=i,i.round=ge(d),i.ceil=ge(e),i.offset=ge(f),i.range=h,a}function ge(a){return function(b,c){try{fs=ft;var d=new ft;return d._=b,a(d,c)._}finally{fs=Date}}}function gf(a,b,c){function d(b){return a(b)}return d.invert=function(b){return gh(a.invert(b))},d.domain=function(b){return arguments.length?(a.domain(b),d):a.domain().map(gh)},d.nice=function(a){var b=gg(d.domain());return d.domain([a.floor(b[0]),a.ceil(b[1])])},d.ticks=function(c,e){var f=gg(d.domain());if(typeof c!="function"){var g=f[1]-f[0],h=g/c,i=d3.bisect(gl,h);if(i==gl.length)return b.year(f,c);if(!i)return a.ticks(c).map(gh);Math.log(h/gl[i-1])<Math.log(gl[i]/h)&&--i,c=b[i],e=c[1],c=c[0].range}return c(f[0],new Date(+f[1]+1),e)},d.tickFormat=function(){return c},d.copy=function(){return gf(a.copy(),b,c)},d3.rebind(d,a,"range","rangeRound","interpolate","clamp")}function gg(a){var b=a[0],c=a[a.length-1];return b<c?[b,c]:[c,b]}function gh(a){return new Date(a)}function gi(a){return function(b){var c=a.length-1,d=a[c];while(!d[1](b))d=a[--c];return d[0](b)}}function gj(a){var b=new Date(a,0,1);return b.setFullYear(a),b}function gk(a){var b=a.getFullYear(),c=gj(b),d=gj(b+1);return b+(a-c)/(d-c)}function gt(a){var b=new Date(Date.UTC(a,0,1));return b.setUTCFullYear(a),b}function gu(a){var b=a.getUTCFullYear(),c=gt(b),d=gt(b+1);return b+(a-c)/(d-c)}Date.now||(Date.now=function(){return+(new Date)});try{document.createElement("div").style.setProperty("opacity",0,"")}catch(a){var b=CSSStyleDeclaration.prototype,c=b.setProperty;b.setProperty=function(a,b,d){c.call(this,a,b+"",d)}}d3={version:"2.9.7"};var e=g;try{e(document.documentElement.childNodes)[0].nodeType}catch(h){e=f}var i=[].__proto__?function(a,b){a.__proto__=b}:function(a,b){for(var c in b)a[c]=b[c]};d3.map=function(a){var b=new j;for(var c in a)b.set(c,a[c]);return b},d(j,{has:function(a){return k+a in this},get:function(a){return this[k+a]},set:function(a,b){return this[k+a]=b},remove:function(a){return a=k+a,a in this&&delete this[a]},keys:function(){var a=[];return this.forEach(function(b){a.push(b)}),a},values:function(){var a=[];return this.forEach(function(b,c){a.push(c)}),a},entries:function(){var a=[];return this.forEach(function(b,c){a.push({key:b,value:c})}),a},forEach:function(a){for(var b in this)b.charCodeAt(0)===l&&a.call(this,b.substring(1),this[b])}});var k="\0",l=k.charCodeAt(0);d3.functor=p,d3.rebind=function(a,b){var c=1,d=arguments.length,e;while(++c<d)a[e=arguments[c]]=q(a,b,b[e]);return a},d3.ascending=function(a,b){return a<b?-1:a>b?1:a>=b?0:NaN},d3.descending=function(a,b){return b<a?-1:b>a?1:b>=a?0:NaN},d3.mean=function(a,b){var c=a.length,d,e=0,f=-1,g=0;if(arguments.length===1)while(++f<c)r(d=a[f])&&(e+=(d-e)/++g);else while(++f<c)r(d=b.call(a,a[f],f))&&(e+=(d-e)/++g);return g?e:undefined},d3.median=function(a,b){return arguments.length>1&&(a=a.map(b)),a=a.filter(r),a.length?d3.quantile(a.sort(d3.ascending),.5):undefined},d3.min=function(a,b){var c=-1,d=a.length,e,f;if(arguments.length===1){while(++c<d&&((e=a[c])==null||e!=e))e=undefined;while(++c<d)(f=a[c])!=null&&e>f&&(e=f)}else{while(++c<d&&((e=b.call(a,a[c],c))==null||e!=e))e=undefined;while(++c<d)(f=b.call(a,a[c],c))!=null&&e>f&&(e=f)}return e},d3.max=function(a,b){var c=-1,d=a.length,e,f;if(arguments.length===1){while(++c<d&&((e=a[c])==null||e!=e))e=undefined;while(++c<d)(f=a[c])!=null&&f>e&&(e=f)}else{while(++c<d&&((e=b.call(a,a[c],c))==null||e!=e))e=undefined;while(++c<d)(f=b.call(a,a[c],c))!=null&&f>e&&(e=f)}return e},d3.extent=function(a,b){var c=-1,d=a.length,e,f,g;if(arguments.length===1){while(++c<d&&((e=g=a[c])==null||e!=e))e=g=undefined;while(++c<d)(f=a[c])!=null&&(e>f&&(e=f),g<f&&(g=f))}else{while(++c<d&&((e=g=b.call(a,a[c],c))==null||e!=e))e=undefined;while(++c<d)(f=b.call(a,a[c],c))!=null&&(e>f&&(e=f),g<f&&(g=f))}return[e,g]},d3.random={normal:function(a,b){return arguments.length<2&&(b=1),arguments.length<1&&(a=0),function(){var c,d,e;do c=Math.random()*2-1,d=Math.random()*2-1,e=c*c+d*d;while(!e||e>1);return a+b*c*Math.sqrt(-2*Math.log(e)/e)}}},d3.sum=function(a,b){var c=0,d=a.length,e,f=-1;if(arguments.length===1)while(++f<d)isNaN(e=+a[f])||(c+=e);else while(++f<d)isNaN(e=+b.call(a,a[f],f))||(c+=e);return c},d3.quantile=function(a,b){var c=(a.length-1)*b+1,d=Math.floor(c),e=a[d-1],f=c-d;return f?e+f*(a[d]-e):e},d3.transpose=function(a){return d3.zip.apply(d3,a)},d3.zip=function(){if(!(e=arguments.length))return[];for(var a=-1,b=d3.min(arguments,s),c=new Array(b);++a<b;)for(var d=-1,e,f=c[a]=new Array(e);++d<e;)f[d]=arguments[d][a];return c},d3.bisector=function(a){return{left:function(b,c,d,e){arguments.length<3&&(d=0),arguments.length<4&&(e=b.length);while(d<e){var f=d+e>>>1;a.call(b,b[f],f)<c?d=f+1:e=f}return d},right:function(b,c,d,e){arguments.length<3&&(d=0),arguments.length<4&&(e=b.length);while(d<e){var f=d+e>>>1;c<a.call(b,b[f],f)?e=f:d=f+1}return d}}};var t=d3.bisector(function(a){return a});d3.bisectLeft=t.left,d3.bisect=d3.bisectRight=t.right,d3.first=function(a,b){var c=0,d=a.length,e=a[0],f;arguments.length===1&&(b=d3.ascending);while(++c<d)b.call(a,e,f=a[c])>0&&(e=f);return e},d3.last=function(a,b){var c=0,d=a.length,e=a[0],f;arguments.length===1&&(b=d3.ascending);while(++c<d)b.call(a,e,f=a[c])<=0&&(e=f);return e},d3.nest=function(){function f(c,g){if(g>=b.length)return e?e.call(a,c):d?c.sort(d):c;var h=-1,i=c.length,k=b[g++],l,m,n=new j,o,p={};while(++h<i)(o=n.get(l=k(m=c[h])))?o.push(m):n.set(l,[m]);return n.forEach(function(a){p[a]=f(n.get(a),g)}),p}function g(a,d){if(d>=b.length)return a;var e=[],f=c[d++],h;for(h in a)e.push({key:h,values:g(a[h],d)});return f&&e.sort(function(a,b){return f(a.key,b.key)}),e}var a={},b=[],c=[],d,e;return a.map=function(a){return f(a,0)},a.entries=function(a){return g(f(a,0),0)},a.key=function(c){return b.push(c),a},a.sortKeys=function(d){return c[b.length-1]=d,a},a.sortValues=function(b){return d=b,a},a.rollup=function(b){return e=b,a},a},d3.keys=function(a){var b=[];for(var c in a)b.push(c);return b},d3.values=function(a){var b=[];for(var c in a)b.push(a[c]);return b},d3.entries=function(a){var b=[];for(var c in a)b.push({key:c,value:a[c]});return b},d3.permute=function(a,b){var c=[],d=-1,e=b.length;while(++d<e)c[d]=a[b[d]];return c},d3.merge=function(a){return Array.prototype.concat.apply([],a)},d3.split=function(a,b){var c=[],d=[],e,f=-1,g=a.length;arguments.length<2&&(b=u);while(++f<g)b.call(d,e=a[f],f)?d=[]:(d.length||c.push(d),d.push(e));return c},d3.range=function(a,b,c){arguments.length<3&&(c=1,arguments.length<2&&(b=a,a=0));if((b-a)/c===Infinity)throw new Error("infinite range");var d=[],e=w(Math.abs(c)),f=-1,g;a*=e,b*=e,c*=e;if(c<0)while((g=a+c*++f)>b)d.push(g/e);else while((g=a+c*++f)<b)d.push(g/e);return d},d3.requote=function(a){return a.replace(x,"\\$&")};var x=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;d3.round=function(a,b){return b?Math.round(a*(b=Math.pow(10,b)))/b:Math.round(a)},d3.xhr=function(a,b,c){var d=new XMLHttpRequest;arguments.length<3?(c=b,b=null):b&&d.overrideMimeType&&d.overrideMimeType(b),d.open("GET",a,!0),b&&d.setRequestHeader("Accept",b),d.onreadystatechange=function(){if(d.readyState===4){var a=d.status;c(!a&&d.response||a>=200&&a<300||a===304?d:null)}},d.send(null)},d3.text=function(a,b,c){function d(a){c(a&&a.responseText)}arguments.length<3&&(c=b,b=null),d3.xhr(a,b,d)},d3.json=function(a,b){d3.text(a,"application/json",function(a){b(a?JSON.parse(a):null)})},d3.html=function(a,b){d3.text(a,"text/html",function(a){if(a!=null){var c=document.createRange();c.selectNode(document.body),a=c.createContextualFragment(a)}b(a)})},d3.xml=function(a,b,c){function d(a){c(a&&a.responseXML)}arguments.length<3&&(c=b,b=null),d3.xhr(a,b,d)};var y={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};d3.ns={prefix:y,qualify:function(a){var b=a.indexOf(":"),c=a;return b>=0&&(c=a.substring(0,b),a=a.substring(b+1)),y.hasOwnProperty(c)?{space:y[c],local:a}:a}},d3.dispatch=function(){var a=new z,b=-1,c=arguments.length;while(++b<c)a[arguments[b]]=A(a);return a},z.prototype.on=function(a,b){var c=a.indexOf("."),d="";return c>0&&(d=a.substring(c+1),a=a.substring(0,c)),arguments.length<2?this[a].on(d):this[a].on(d,b)},d3.format=function(a){var b=B.exec(a),c=b[1]||" ",d=b[3]||"",e=b[5],f=+b[6],g=b[7],h=b[8],i=b[9],j=1,k="",l=!1;h&&(h=+h.substring(1)),e&&(c="0",g&&(f-=Math.floor((f-1)/4)));switch(i){case"n":g=!0,i="g";break;case"%":j=100,k="%",i="f";break;case"p":j=100,k="%",i="r";break;case"d":l=!0,h=0;break;case"s":j=-1,i="r"}return i=="r"&&!h&&(i="g"),i=C.get(i)||E,function(a){if(l&&a%1)return"";var b=a<0&&(a=-a)?"−":d;if(j<0){var m=d3.formatPrefix(a,h);a=m.scale(a),k=m.symbol}else a*=j;a=i(a,h);if(e){var n=a.length+b.length;n<f&&(a=(new Array(f-n+1)).join(c)+a),g&&(a=F(a)),a=b+a}else{g&&(a=F(a)),a=b+a;var n=a.length;n<f&&(a=(new Array(f-n+1)).join(c)+a)}return a+k}};var B=/(?:([^{])?([<>=^]))?([+\- ])?(#)?(0)?([0-9]+)?(,)?(\.[0-9]+)?([a-zA-Z%])?/,C=d3.map({g:function(a,b){return a.toPrecision(b)},e:function(a,b){return a.toExponential(b)},f:function(a,b){return a.toFixed(b)},r:function(a,b){return d3.round(a,b=D(a,b)).toFixed(Math.max(0,Math.min(20,b)))}}),G=["y","z","a","f","p","n","μ","m","","k","M","G","T","P","E","Z","Y"].map(H);d3.formatPrefix=function(a,b){var c=0;return a&&(a<0&&(a*=-1),b&&(a=d3.round(a,D(a,b))),c=1+Math.floor(1e-12+Math.log(a)/Math.LN10),c=Math.max(-24,Math.min(24,Math.floor((c<=0?c+1:c-1)/3)*3))),G[8+c/3]};var I=R(2),J=R(3),K=function(){return Q},L=d3.map({linear:K,poly:R,quad:function(){return I},cubic:function(){return J},sin:function(){return S},exp:function(){return T},circle:function(){return U},elastic:V,back:W,bounce:function(){return X}}),M=d3.map({"in":Q,out:O,"in-out":P,"out-in":function(a){return P(O(a))}});d3.ease=function(a){var b=a.indexOf("-"),c=b>=0?a.substring(0,b):a,d=b>=0?a.substring(b+1):"in";return c=L.get(c)||K,d=M.get(d)||Q,N(d(c.apply(null,Array.prototype.slice.call(arguments,1))))},d3.event=null,d3.transform=function(a){var b=document.createElementNS(d3.ns.prefix.svg,"g");return(d3.transform=function(a){b.setAttribute("transform",a);var c=b.transform.baseVal.consolidate();return new _(c?c.matrix:be)})(a)},_.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var bd=180/Math.PI,be={a:1,b:0,c:0,d:1,e:0,f:0};d3.interpolate=function(a,b){var c=d3.interpolators.length,d;while(--c>=0&&!(d=d3.interpolators[c](a,b)));return d},d3.interpolateNumber=function(a,b){return b-=a,function(c){return a+b*c}},d3.interpolateRound=function(a,b){return b-=a,function(c){return Math.round(a+b*c)}},d3.interpolateString=function(a,b){var c,d,e,f=0,g=0,h=[],i=[],j,k;bi.lastIndex=0;for(d=0;c=bi.exec(b);++d)c.index&&h.push(b.substring(f,g=c.index)),i.push({i:h.length,x:c[0]}),h.push(null),f=bi.lastIndex;f<b.length&&h.push(b.substring(f));for(d=0,j=i.length;(c=bi.exec(a))&&d<j;++d){k=i[d];if(k.x==c[0]){if(k.i)if(h[k.i+1]==null){h[k.i-1]+=k.x,h.splice(k.i,1);for(e=d+1;e<j;++e)i[e].i--}else{h[k.i-1]+=k.x+h[k.i+1],h.splice(k.i,2);for(e=d+1;e<j;++e)i[e].i-=2}else if(h[k.i+1]==null)h[k.i]=k.x;else{h[k.i]=k.x+h[k.i+1],h.splice(k.i+1,1);for(e=d+1;e<j;++e)i[e].i--}i.splice(d,1),j--,d--}else k.x=d3.interpolateNumber(parseFloat(c[0]),parseFloat(k.x))}while(d<j)k=i.pop(),h[k.i+1]==null?h[k.i]=k.x:(h[k.i]=k.x+h[k.i+1],h.splice(k.i+1,1)),j--;return h.length===1?h[0]==null?i[0].x:function(){return b}:function(a){for(d=0;d<j;++d)h[(k=i[d]).i]=k.x(a);return h.join("")}},d3.interpolateTransform=function(a,b){if(e=bg(a,b))return e;var c=[],d=[],e,f=d3.transform(a),g=d3.transform(b),h=f.translate,i=g.translate,j=f.rotate,k=g.rotate,l=f.skew,m=g.skew,n=f.scale,o=g.scale;return h[0]!=i[0]||h[1]!=i[1]?(c.push("translate(",null,",",null,")"),d.push({i:1,x:d3.interpolateNumber(h[0],i[0])},{i:3,x:d3.interpolateNumber(h[1],i[1])})):i[0]||i[1]?c.push("translate("+i+")"):c.push(""),j!=k?(j-k>180?k+=360:k-j>180&&(j+=360),d.push({i:c.push(c.pop()+"rotate(",null,")")-2,x:d3.interpolateNumber(j,k)})):k&&c.push(c.pop()+"rotate("+k+")"),l!=m?d.push({i:c.push(c.pop()+"skewX(",null,")")-2,x:d3.interpolateNumber(l,m)}):m&&c.push(c.pop()+"skewX("+m+")"),n[0]!=o[0]||n[1]!=o[1]?(e=c.push(c.pop()+"scale(",null,",",null,")"),d.push({i:e-4,x:d3.interpolateNumber(n[0],o[0])},{i:e-2,x:d3.interpolateNumber(n[1],o[1])})):(o[0]!=1||o[1]!=1)&&c.push(c.pop()+"scale("+o+")"),e=d.length,function(a){var b=-1,f;while(++b<e)c[(f=d[b]).i]=f.x(a);return c.join("")}};var bf=["","","translate","scale","rotate","skewX","skewY"],bg=function(a,b){var c=document.createElementNS(d3.ns.prefix.svg,"g"),d=document.createElementNS(d3.ns.prefix.svg,"g");return(bg=function(a,b){c.setAttribute("transform",a),d.setAttribute("transform",b),a=c.transform.baseVal,b=d.transform.baseVal;var e=[],f=[],g=-1,h=a.numberOfItems,i=b.numberOfItems,j,k,l;if(i!==h)if(!i)b=bh(a);else if(!h)a=bh(b),h=i;else return;else if(!i)return;while(++g<h){j=a.getItem(g),k=b.getItem(g),l=j.type;if(l!==k.type||!l)return;switch(l){case 1:e.push(new _(j.matrix)),f.push(new _(k.matrix));continue;case 2:ra=j.matrix.e+","+j.matrix.f,rb=k.matrix.e+","+k.matrix.f;break;case 3:ra=j.matrix.a+","+j.matrix.d,rb=k.matrix.a+","+k.matrix.d;break;default:ra=j.angle,rb=k.angle}e.push(l=bf[l],"(",ra,")"),f.push(l,"(",rb,")")}return d3.interpolateString(e.join(""),f.join(""))})(a,b)};d3.interpolateRgb=function(a,b){a=d3.rgb(a),b=d3.rgb(b);var c=a.r,d=a.g,e=a.b,f=b.r-c,g=b.g-d,h=b.b-e;return function(a){return"#"+bo(Math.round(c+f*a))+bo(Math.round(d+g*a))+bo(Math.round(e+h*a))}},d3.interpolateHsl=function(a,b){a=d3.hsl(a),b=d3.hsl(b);var c=a.h,d=a.s,e=a.l,f=b.h-c,g=b.s-d,h=b.l-e;return f>180?f-=360:f<-180&&(f+=360),function(a){return bv(c+f*a,d+g*a,e+h*a).toString()}},d3.interpolateArray=function(a,b){var c=[],d=[],e=a.length,f=b.length,g=Math.min(a.length,b.length),h;for(h=0;h<g;++h)c.push(d3.interpolate(a[h],b[h]));for(;h<e;++h)d[h]=a[h];for(;h<f;++h)d[h]=b[h];return function(a){for(h=0;h<g;++h)d[h]=c[h](a);return d}},d3.interpolateObject=function(a,b){var c={},d={},e;for(e in a)e in b?c[e]=bj(e)(a[e],b[e]):d[e]=a[e];for(e in b)e in a||(d[e]=b[e]);return function(a){for(e in c)d[e]=c[e](a);return d}};var bi=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;d3.interpolators=[d3.interpolateObject,function(a,b){return b instanceof Array&&d3.interpolateArray(a,b)},function(a,b){return(typeof a=="string"||typeof b=="string")&&d3.interpolateString(a+"",b+"")},function(a,b){return(typeof b=="string"?bs.has(b)||/^(#|rgb\(|hsl\()/.test(b):b instanceof bn||b instanceof bu)&&d3.interpolateRgb(a,b)},function(a,b){return!isNaN(a=+a)&&!isNaN(b=+b)&&d3.interpolateNumber(a,b)}],d3.rgb=function(a,b,c){return arguments.length===1?a instanceof bn?bm(a.r,a.g,a.b):bp(""+a,bm,bv):bm(~~a,~~b,~~c)},bn.prototype.brighter=function(a){a=Math.pow(.7,arguments.length?a:1);var b=this.r,c=this.g,d=this.b,e=30;return!b&&!c&&!d?bm(e,e,e):(b&&b<e&&(b=e),c&&c<e&&(c=e),d&&d<e&&(d=e),bm(Math.min(255,Math.floor(b/a)),Math.min(255,Math.floor(c/a)),Math.min(255,Math.floor(d/a))))},bn.prototype.darker=function(a){return a=Math.pow(.7,arguments.length?a:1),bm(Math.floor(a*this.r),Math.floor(a*this.g),Math.floor(a*this.b))},bn.prototype.hsl=function(){return bq(this.r,this.g,this.b)},bn.prototype.toString=function(){return"#"+bo(this.r)+bo(this.g)+bo(this.b)};var bs=d3.map({aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"});bs.forEach(function(a,b){bs.set(a,bp(b,bm,bv))}),d3.hsl=function(a,b,c){return arguments.length===1?a instanceof bu?bt(a.h,a.s,a.l):bp(""+a,bq,bt):bt(+a,+b,+c)},bu.prototype.brighter=function(a){return a=Math.pow(.7,arguments.length?a:1),bt(this.h,this.s,this.l/a)},bu.prototype.darker=function(a){return a=Math.pow(.7,arguments.length?a:1),bt(this.h,this.s,a*this.l)},bu.prototype.rgb=function(){return bv(this.h,this.s,this.l)},bu.prototype.toString=function(){return this.rgb().toString()};var bx=function(a,b){return b.querySelector(a)},by=function(a,b){return b.querySelectorAll(a)},bz=document.documentElement,bA=bz.matchesSelector||bz.webkitMatchesSelector||bz.mozMatchesSelector||bz.msMatchesSelector||bz.oMatchesSelector,bB=function(a,b){return bA.call(a,b)};typeof Sizzle=="function"&&(bx=function(a,b){return Sizzle(a,b)[0]||null},by=function(a,b){return Sizzle.uniqueSort(Sizzle(a,b))},bB=Sizzle.matchesSelector);var bC=[];d3.selection=function(){return bK},d3.selection.prototype=bC,bC.select=function(a){var b=[],c,d,e,f;typeof a!="function"&&(a=bD(a));for(var g=-1,h=this.length;++g<h;){b.push(c=[]),c.parentNode=(e=this[g]).parentNode;for(var i=-1,j=e.length;++i<j;)(f=e[i])?(c.push(d=a.call(f,f.__data__,i)),d&&"__data__"in f&&(d.__data__=f.__data__)):c.push(null)}return bw(b)},bC.selectAll=function(a){var b=[],c,d;typeof a!="function"&&(a=bE(a));for(var f=-1,g=this.length;++f<g;)for(var h=this[f],i=-1,j=h.length;++i<j;)if(d=h[i])b.push(c=e(a.call(d,d.__data__,i))),c.parentNode=d;return bw(b)},bC.attr=function(a,b){function d(){this.removeAttribute(a)}function e(){this.removeAttributeNS(a.space,a.local)}function f(){this.setAttribute(a,b)}function g(){this.setAttributeNS(a.space,a.local,b)}function h(){var c=b.apply(this,arguments);c==null?this.removeAttribute(a):this.setAttribute(a,c)}function i(){var c=b.apply(this,arguments);c==null?this.removeAttributeNS(a.space,a.local):this.setAttributeNS(a.space,a.local,c)}a=d3.ns.qualify(a);if(arguments.length<2){var c=this.node();return a.local?c.getAttributeNS(a.space,a.local):c.getAttribute(a)}return this.each(b==null?a.local?e:d:typeof b=="function"?a.local?i:h:a.local?g:f)},bC.classed=function(a,b){var c=v(a).split(" "),d=c.length,e=-1;if(arguments.length>1){while(++e<d)bF.call(this,c[e],b);return this}while(++e<d)if(!bF.call(this,c[e]))return!1;return!0},bC.style=function(a,b,c){function d(){this.style.removeProperty(a)}function e(){this.style.setProperty(a,b,c)}function f(){var d=b.apply(this,arguments);d==null?this.style.removeProperty(a):this.style.setProperty(a,d,c)}return arguments.length<3&&(c=""),arguments.length<2?window.getComputedStyle(this.node(),null).getPropertyValue(a):this.each(b==null?d:typeof b=="function"?f:e)},bC.property=function(a,b){function c(){delete this[a]}function d(){this[a]=b}function e(){var c=b.apply(this,arguments);c==null?delete this[a]:this[a]=c}return arguments.length<2?this.node()[a]:this.each(b==null?c:typeof b=="function"?e:d)},bC.text=function(a){return arguments.length<1?this.node().textContent:this.each(typeof a=="function"?function(){var b=a.apply(this,arguments);this.textContent=b==null?"":b}:a==null?function(){this.textContent=""}:function(){this.textContent=a})},bC.html=function(a){return arguments.length<1?this.node().innerHTML:this.each(typeof a=="function"?function(){var b=a.apply(this,arguments);this.innerHTML=b==null?"":b}:a==null?function(){this.innerHTML=""}:function(){this.innerHTML=a})},bC.append=function(a){function b(){return this.appendChild(document.createElementNS(this.namespaceURI,a))}function c(){return this.appendChild(document.createElementNS(a.space,a.local))}return a=d3.ns.qualify(a),this.select(a.local?c:b)},bC.insert=function(a,b){function c(){return this.insertBefore(document.createElementNS(this.namespaceURI,a),bx(b,this))}function d(){return this.insertBefore(document.createElementNS(a.space,a.local),bx(b,this))}return a=d3.ns.qualify(a),this.select(a.local?d:c)},bC.remove=function(){return this.each(function(){var a=this.parentNode;a&&a.removeChild(this)})},bC.data=function(a,b){function g(a,c){var d,e=a.length,f=c.length,g=Math.min(e,f),l=Math.max(e,f),m=[],n=[],o=[],p,q;if(b){var r=new j,s=[],t,u=c.length;for(d=-1;++d<e;)t=b.call(p=a[d],p.__data__,d),r.has(t)?o[u++]=p:r.set(t,p),s.push(t);for(d=-1;++d<f;)t=b.call(c,q=c[d],d),r.has(t)?(m[d]=p=r.get(t),p.__data__=q,n[d]=o[d]=null):(n[d]=bG(q),m[d]=o[d]=null),r.remove(t);for(d=-1;++d<e;)r.has(s[d])&&(o[d]=a[d])}else{for(d=-1;++d<g;)p=a[d],q=c[d],p?(p.__data__=q,m[d]=p,n[d]=o[d]=null):(n[d]=bG(q),m[d]=o[d]=null);for(;d<f;++d)n[d]=bG(c[d]),m[d]=o[d]=null;for(;d<l;++d)o[d]=a[d],n[d]=m[d]=null}n.update=m,n.parentNode=m.parentNode=o.parentNode=a.parentNode,h.push(n),i.push(m),k.push(o)}var c=-1,d=this.length,e,f;if(!arguments.length){a=new Array(d=(e=this[0]).length);while(++c<d)if(f=e[c])a[c]=f.__data__;return a}var h=bL([]),i=bw([]),k=bw([]);if(typeof a=="function")while(++c<d)g(e=this[c],a.call(e,e.parentNode.__data__,c));else while(++c<d)g(e=this[c],a);return i.enter=function(){return h},i.exit=function(){return k},i},bC.datum=bC.map=function(a){return arguments.length<1?this.property("__data__"):this.property("__data__",a)},bC.filter=function(a){var b=[],c,d,e;typeof a!="function"&&(a=bH(a));for(var f=0,g=this.length;f<g;f++){b.push(c=[]),c.parentNode=(d=this[f]).parentNode;for(var h=0,i=d.length;h<i;h++)(e=d[h])&&a.call(e,e.__data__,h)&&c.push(e)}return bw(b)},bC.order=function(){for(var a=-1,b=this.length;++a<b;)for(var c=this[a],d=c.length-1,e=c[d],f;--d>=0;)if(f=c[d])e&&e!==f.nextSibling&&e.parentNode.insertBefore(f,e),e=f;return this},bC.sort=function(a){a=bI.apply(this,arguments);for(var b=-1,c=this.length;++b<c;)this[b].sort(a);return this.order()},bC.on=function(a,b,c){arguments.length<3&&(c=!1);var d="__on"+a,e=a.indexOf(".");return e>0&&(a=a.substring(0,e)),arguments.length<2?(e=this.node()[d])&&e._:this.each(function(){function h(a){var c=d3.event;d3.event=a,f[0]=e.__data__;try{b.apply(e,f)}finally{d3.event=c}}var e=this,f=arguments,g=e[d];g&&(e.removeEventListener(a,g,g.$),delete e[d]),b&&(e.addEventListener(a,e[d]=h,h.$=c),h._=b)})},bC.each=function(a){return bJ(this,function(b,c,d){a.call(b,b.__data__,c,d)})},bC.call=function(a){return a.apply(this,(arguments[0]=this,arguments)),this},bC.empty=function(){return!this.node()},bC.node=function(a){for(var b=0,c=this.length;b<c;b++)for(var d=this[b],e=0,f=d.length;e<f;e++){var g=d[e];if(g)return g}return null},bC.transition=function(){var a=[],b,c;for(var d=-1,e=this.length;++d<e;){a.push(b=[]);for(var f=this[d],g=-1,h=f.length;++g<h;)b.push((c=f[g])?{node:c,delay:bX,duration:bY}:null)}return bN(a,bT||++bS,Date.now())};var bK=bw([[document]]);bK[0].parentNode=bz,d3.select=function(a){return typeof a=="string"?bK.select(a):bw([[a]])},d3.selectAll=function(a){return typeof a=="string"?bK.selectAll(a):bw([e(a)])};var bM=[];d3.selection.enter=bL,d3.selection.enter.prototype=bM,bM.append=bC.append,bM.insert=bC.insert,bM.empty=bC.empty,bM.node=bC.node,bM.select=function(a){var b=[],c,d,e,f,g;for(var h=-1,i=this.length;++h<i;){e=(f=this[h]).update,b.push(c=[]),c.parentNode=f.parentNode;for(var j=-1,k=f.length;++j<k;)(g=f[j])?(c.push(e[j]=d=a.call(f.parentNode,g.__data__,j)),d.__data__=g.__data__):c.push(null)}return bw(b)};var bO={},bR=[],bS=0,bT=0,bU=0,bV=250,bW=d3.ease("cubic-in-out"),bX=bU,bY=bV,bZ=bW;bR.call=bC.call,d3.transition=function(a){return arguments.length?bT?a.transition():a:bK.transition()},d3.transition.prototype=bR,bR.select=function(a){var b=[],c,d,e;typeof a!="function"&&(a=bD(a));for(var f=-1,g=this.length;++f<g;){b.push(c=[]);for(var h=this[f],i=-1,j=h.length;++i<j;)(e=h[i])&&(d=a.call(e.node,e.node.__data__,i))?("__data__"in e.node&&(d.__data__=e.node.__data__),c.push({node:d,delay:e.delay,duration:e.duration})):c.push(null)}return bN(b,this.id,this.time).ease(this.ease())},bR.selectAll=function(a){var b=[],c,d,e;typeof a!="function"&&(a=bE(a));for(var f=-1,g=this.length;++f<g;)for(var h=this[f],i=-1,j=h.length;++i<j;)if(e=h[i]){d=a.call(e.node,e.node.__data__,i),b.push(c=[]);for(var k=-1,l=d.length;++k<l;)c.push({node:d[k],delay:e.delay,duration:e.duration})}return bN(b,this.id,this.time).ease(this.ease())},bR.filter=function(a){var b=[],c,d,e;typeof a!="function"&&(a=bH(a));for(var f=0,g=this.length;f<g;f++){b.push(c=[]);for(var d=this[f],h=0,i=d.length;h<i;h++)(e=d[h])&&a.call(e.node,e.node.__data__,h)&&c.push(e)}return bN(b,this.id,this.time).ease(this.ease())},bR.attr=function(a,b){return this.attrTween(a,bQ(a,b))},bR.attrTween=function(a,b){function d(a,d){var e=b.call(this,a,d,this.getAttribute(c));return e===bO?(this.removeAttribute(c),null):e&&function(a){this.setAttribute(c,e(a))}}function e(a,d){var e=b.call(this,a,d,this.getAttributeNS(c.space,c.local));return e===bO?(this.removeAttributeNS(c.space,c.local),null):e&&function(a){this.setAttributeNS(c.space,c.local,e(a))}}var c=d3.ns.qualify(a);return this.tween("attr."+a,c.local?e:d)},bR.style=function(a,b,c){return arguments.length<3&&(c=""),this.styleTween(a,bQ(a,b),c)},bR.styleTween=function(a,b,c){return arguments.length<3&&(c=""),this.tween("style."+a,function(d,e){var f=b.call(this,d,e,window.getComputedStyle(this,null).getPropertyValue(a));return f===bO?(this.style.removeProperty(a),null):f&&function(b){this.style.setProperty(a,f(b),c)}})},bR.text=function(a){return this.tween("text",function(b,c){this.textContent=typeof a=="function"?a.call(this,b,c):a})},bR.remove=function(){return this.each("end.transition",function(){var a;!this.__transition__&&(a=this.parentNode)&&a.removeChild(this)})},bR.delay=function(a){return bJ(this,typeof a=="function"?function(b,c,d){b.delay=a.call(b=b.node,b.__data__,c,d)|0}:(a|=0,function(b){b.delay=a}))},bR.duration=function(a){return bJ(this,typeof a=="function"?function(b,c,d){b.duration=Math.max(1,a.call(b=b.node,b.__data__,c,d)|0)}:(a=Math.max(1,a|0),function(b){b.duration=a}))},bR.transition=function(){return this.select(n)};var b_=null,ca,cb;d3.timer=function(a,b,c){var d=!1,e,f=b_;if(arguments.length<3){if(arguments.length<2)b=0;else if(!isFinite(b))return;c=Date.now()}while(f){if(f.callback===a){f.then=c,f.delay=b,d=!0;break}e=f,f=f.next}d||(b_={callback:a,then:c,delay:b,next:b_}),ca||(cb=clearTimeout(cb),ca=1,ce(cc))},d3.timer.flush=function(){var a,b=Date.now(),c=b_;while(c)a=b-c.then,c.delay||(c.flush=c.callback(a)),c=c.next;cd()};var ce=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){setTimeout(a,17)};d3.mouse=function(a){return cg(a,Z())};var cf=/WebKit/.test(navigator.userAgent)?-1:0;d3.touches=function(a,b){return arguments.length<2&&(b=Z().touches),b?e(b).map(function(b){var c=cg(a,b);return c.identifier=b.identifier,c}):[]},d3.scale={},d3.scale.linear=function(){return cm([0,1],[0,1],d3.interpolate,!1)},d3.scale.log=function(){return cu(d3.scale.linear(),cw)};var cv=d3.format(".0e");cw.pow=function(a){return Math.pow(10,a)},cx.pow=function(a){return-Math.pow(10,-a)},d3.scale.pow=function(){return cy(d3.scale.linear(),1)},d3.scale.sqrt=function(){return d3.scale.pow().exponent(.5)},d3.scale.ordinal=function(){return cA([],{t:"range",x:[]})},d3.scale.category10=function(){return d3.scale.ordinal().range(cB)},d3.scale.category20=function(){return d3.scale.ordinal().range(cC)},d3.scale.category20b=function(){return d3.scale.ordinal().range(cD)},d3.scale.category20c=function(){return d3.scale.ordinal().range(cE)};var cB=["#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd","#8c564b","#e377c2","#7f7f7f","#bcbd22","#17becf"],cC=["#1f77b4","#aec7e8","#ff7f0e","#ffbb78","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5","#8c564b","#c49c94","#e377c2","#f7b6d2","#7f7f7f","#c7c7c7","#bcbd22","#dbdb8d","#17becf","#9edae5"],cD=["#393b79","#5254a3","#6b6ecf","#9c9ede","#637939","#8ca252","#b5cf6b","#cedb9c","#8c6d31","#bd9e39","#e7ba52","#e7cb94","#843c39","#ad494a","#d6616b","#e7969c","#7b4173","#a55194","#ce6dbd","#de9ed6"],cE=["#3182bd","#6baed6","#9ecae1","#c6dbef","#e6550d","#fd8d3c","#fdae6b","#fdd0a2","#31a354","#74c476","#a1d99b","#c7e9c0","#756bb1","#9e9ac8","#bcbddc","#dadaeb","#636363","#969696","#bdbdbd","#d9d9d9"];d3.scale.quantile=function(){return cF([],[])},d3.scale.quantize=function(){return cG(0,1,[0,1])},d3.scale.identity=function(){return cH([0,1])},d3.svg={},d3.svg.arc=function(){function e(){var e=a.apply(this,arguments),f=b.apply(this,arguments),g=c.apply(this,arguments)+cI,h=d.apply(this,arguments)+cI,i=(h<g&&(i=g,g=h,h=i),h-g),j=i<Math.PI?"0":"1",k=Math.cos(g),l=Math.sin(g),m=Math.cos(h),n=Math.sin(h);return i>=cJ?e?"M0,"+f+"A"+f+","+f+" 0 1,1 0,"+ -f+"A"+f+","+f+" 0 1,1 0,"+f+"M0,"+e+"A"+e+","+e+" 0 1,0 0,"+ -e+"A"+e+","+e+" 0 1,0 0,"+e+"Z":"M0,"+f+"A"+f+","+f+" 0 1,1 0,"+ -f+"A"+f+","+f+" 0 1,1 0,"+f+"Z":e?"M"+f*k+","+f*l+"A"+f+","+f+" 0 "+j+",1 "+f*m+","+f*n+"L"+e*m+","+e*n+"A"+e+","+e+" 0 "+j+",0 "+e*k+","+e*l+"Z":"M"+f*k+","+f*l+"A"+f+","+f+" 0 "+j+",1 "+f*m+","+f*n+"L0,0"+"Z"}var a=cK,b=cL,c=cM,d=cN;return e.innerRadius=function(b){return arguments.length?(a=p(b),e):a},e.outerRadius=function(a){return arguments.length?(b=p(a),e):b},e.startAngle=function(a){return arguments.length?(c=p(a),e):c},e.endAngle=function(a){return arguments.length?(d=p(a),e):d},e.centroid=function(){var e=(a.apply(this,arguments)+b.apply(this,arguments))/2,f=(c.apply(this,arguments)+d.apply(this,arguments))/2+cI;return[Math.cos(f)*e,Math.sin(f)*e]},e};var cI=-Math.PI/2,cJ=2*Math.PI-1e-6;d3.svg.line=function(){return cO(m)};var cR="linear",cS=d3.map({linear:cT,"linear-closed":cU,"step-before":cV,"step-after":cW,basis:da,"basis-open":db,"basis-closed":dc,bundle:dd,cardinal:cZ,"cardinal-open":cX,"cardinal-closed":cY,monotone:dm}),df=[0,2/3,1/3,0],dg=[0,1/3,2/3,0],dh=[0,1/6,2/3,1/6];d3.svg.line.radial=function(){var a=cO(dn);return a.radius=a.x,delete a.x,a.angle=a.y,delete a.y,a},cV.reverse=cW,cW.reverse=cV,d3.svg.area=function(){return dp(Object)},d3.svg.area.radial=function(){var a=dp(dn);return a.radius=a.x,delete a.x,a.innerRadius=a.x0,delete a.x0,a.outerRadius=a.x1,delete a.x1,a.angle=a.y,delete a.y,a.startAngle=a.y0,delete a.y0,a.endAngle=a.y1,delete a.y1,a},d3.svg.chord=function(){function f(c,d){var e=g(this,a,c,d),f=g(this,b,c,d |
2561 | +);return"M"+e.p0+i(e.r,e.p1,e.a1-e.a0)+(h(e,f)?j(e.r,e.p1,e.r,e.p0):j(e.r,e.p1,f.r,f.p0)+i(f.r,f.p1,f.a1-f.a0)+j(f.r,f.p1,e.r,e.p0))+"Z"}function g(a,b,f,g){var h=b.call(a,f,g),i=c.call(a,h,g),j=d.call(a,h,g)+cI,k=e.call(a,h,g)+cI;return{r:i,a0:j,a1:k,p0:[i*Math.cos(j),i*Math.sin(j)],p1:[i*Math.cos(k),i*Math.sin(k)]}}function h(a,b){return a.a0==b.a0&&a.a1==b.a1}function i(a,b,c){return"A"+a+","+a+" 0 "+ +(c>Math.PI)+",1 "+b}function j(a,b,c,d){return"Q 0,0 "+d}var a=dq,b=dr,c=ds,d=cM,e=cN;return f.radius=function(a){return arguments.length?(c=p(a),f):c},f.source=function(b){return arguments.length?(a=p(b),f):a},f.target=function(a){return arguments.length?(b=p(a),f):b},f.startAngle=function(a){return arguments.length?(d=p(a),f):d},f.endAngle=function(a){return arguments.length?(e=p(a),f):e},f},d3.svg.diagonal=function(){function d(d,e){var f=a.call(this,d,e),g=b.call(this,d,e),h=(f.y+g.y)/2,i=[f,{x:f.x,y:h},{x:g.x,y:h},g];return i=i.map(c),"M"+i[0]+"C"+i[1]+" "+i[2]+" "+i[3]}var a=dq,b=dr,c=dv;return d.source=function(b){return arguments.length?(a=p(b),d):a},d.target=function(a){return arguments.length?(b=p(a),d):b},d.projection=function(a){return arguments.length?(c=a,d):c},d},d3.svg.diagonal.radial=function(){var a=d3.svg.diagonal(),b=dv,c=a.projection;return a.projection=function(a){return arguments.length?c(dw(b=a)):b},a},d3.svg.mouse=d3.mouse,d3.svg.touches=d3.touches,d3.svg.symbol=function(){function c(c,d){return(dA.get(a.call(this,c,d))||dz)(b.call(this,c,d))}var a=dy,b=dx;return c.type=function(b){return arguments.length?(a=p(b),c):a},c.size=function(a){return arguments.length?(b=p(a),c):b},c};var dA=d3.map({circle:dz,cross:function(a){var b=Math.sqrt(a/5)/2;return"M"+ -3*b+","+ -b+"H"+ -b+"V"+ -3*b+"H"+b+"V"+ -b+"H"+3*b+"V"+b+"H"+b+"V"+3*b+"H"+ -b+"V"+b+"H"+ -3*b+"Z"},diamond:function(a){var b=Math.sqrt(a/(2*dC)),c=b*dC;return"M0,"+ -b+"L"+c+",0"+" 0,"+b+" "+ -c+",0"+"Z"},square:function(a){var b=Math.sqrt(a)/2;return"M"+ -b+","+ -b+"L"+b+","+ -b+" "+b+","+b+" "+ -b+","+b+"Z"},"triangle-down":function(a){var b=Math.sqrt(a/dB),c=b*dB/2;return"M0,"+c+"L"+b+","+ -c+" "+ -b+","+ -c+"Z"},"triangle-up":function(a){var b=Math.sqrt(a/dB),c=b*dB/2;return"M0,"+ -c+"L"+b+","+c+" "+ -b+","+c+"Z"}});d3.svg.symbolTypes=dA.keys();var dB=Math.sqrt(3),dC=Math.tan(30*Math.PI/180);d3.svg.axis=function(){function k(k){k.each(function(){var k=d3.select(this),l=h==null?a.ticks?a.ticks.apply(a,g):a.domain():h,m=i==null?a.tickFormat?a.tickFormat.apply(a,g):String:i,n=dF(a,l,j),o=k.selectAll(".minor").data(n,String),p=o.enter().insert("line","g").attr("class","tick minor").style("opacity",1e-6),q=d3.transition(o.exit()).style("opacity",1e-6).remove(),r=d3.transition(o).style("opacity",1),s=k.selectAll("g").data(l,String),t=s.enter().insert("g","path").style("opacity",1e-6),u=d3.transition(s.exit()).style("opacity",1e-6).remove(),v=d3.transition(s).style("opacity",1),w,x=cj(a),y=k.selectAll(".domain").data([0]),z=y.enter().append("path").attr("class","domain"),A=d3.transition(y),B=a.copy(),C=this.__chart__||B;this.__chart__=B,t.append("line").attr("class","tick"),t.append("text");var D=t.select("line"),E=v.select("line"),F=s.select("text").text(m),G=t.select("text"),H=v.select("text");switch(b){case"bottom":w=dD,p.attr("y2",d),r.attr("x2",0).attr("y2",d),D.attr("y2",c),G.attr("y",Math.max(c,0)+f),E.attr("x2",0).attr("y2",c),H.attr("x",0).attr("y",Math.max(c,0)+f),F.attr("dy",".71em").attr("text-anchor","middle"),A.attr("d","M"+x[0]+","+e+"V0H"+x[1]+"V"+e);break;case"top":w=dD,p.attr("y2",-d),r.attr("x2",0).attr("y2",-d),D.attr("y2",-c),G.attr("y",-(Math.max(c,0)+f)),E.attr("x2",0).attr("y2",-c),H.attr("x",0).attr("y",-(Math.max(c,0)+f)),F.attr("dy","0em").attr("text-anchor","middle"),A.attr("d","M"+x[0]+","+ -e+"V0H"+x[1]+"V"+ -e);break;case"left":w=dE,p.attr("x2",-d),r.attr("x2",-d).attr("y2",0),D.attr("x2",-c),G.attr("x",-(Math.max(c,0)+f)),E.attr("x2",-c).attr("y2",0),H.attr("x",-(Math.max(c,0)+f)).attr("y",0),F.attr("dy",".32em").attr("text-anchor","end"),A.attr("d","M"+ -e+","+x[0]+"H0V"+x[1]+"H"+ -e);break;case"right":w=dE,p.attr("x2",d),r.attr("x2",d).attr("y2",0),D.attr("x2",c),G.attr("x",Math.max(c,0)+f),E.attr("x2",c).attr("y2",0),H.attr("x",Math.max(c,0)+f).attr("y",0),F.attr("dy",".32em").attr("text-anchor","start"),A.attr("d","M"+e+","+x[0]+"H0V"+x[1]+"H"+e)}if(a.ticks)t.call(w,C),v.call(w,B),u.call(w,B),p.call(w,C),r.call(w,B),q.call(w,B);else{var I=B.rangeBand()/2,J=function(a){return B(a)+I};t.call(w,J),v.call(w,J)}})}var a=d3.scale.linear(),b="bottom",c=6,d=6,e=6,f=3,g=[10],h=null,i,j=0;return k.scale=function(b){return arguments.length?(a=b,k):a},k.orient=function(a){return arguments.length?(b=a,k):b},k.ticks=function(){return arguments.length?(g=arguments,k):g},k.tickValues=function(a){return arguments.length?(h=a,k):h},k.tickFormat=function(a){return arguments.length?(i=a,k):i},k.tickSize=function(a,b,f){if(!arguments.length)return c;var g=arguments.length-1;return c=+a,d=g>1?+b:c,e=g>0?+arguments[g]:c,k},k.tickPadding=function(a){return arguments.length?(f=+a,k):f},k.tickSubdivide=function(a){return arguments.length?(j=+a,k):j},k},d3.svg.brush=function(){function g(a){a.each(function(){var a=d3.select(this),e=a.selectAll(".background").data([0]),f=a.selectAll(".extent").data([0]),l=a.selectAll(".resize").data(d,String),m;a.style("pointer-events","all").on("mousedown.brush",k).on("touchstart.brush",k),e.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),f.enter().append("rect").attr("class","extent").style("cursor","move"),l.enter().append("g").attr("class",function(a){return"resize "+a}).style("cursor",function(a){return dG[a]}).append("rect").attr("x",function(a){return/[ew]$/.test(a)?-3:null}).attr("y",function(a){return/^[ns]/.test(a)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),l.style("display",g.empty()?"none":null),l.exit().remove(),b&&(m=cj(b),e.attr("x",m[0]).attr("width",m[1]-m[0]),i(a)),c&&(m=cj(c),e.attr("y",m[0]).attr("height",m[1]-m[0]),j(a)),h(a)})}function h(a){a.selectAll(".resize").attr("transform",function(a){return"translate("+e[+/e$/.test(a)][0]+","+e[+/^s/.test(a)][1]+")"})}function i(a){a.select(".extent").attr("x",e[0][0]),a.selectAll(".extent,.n>rect,.s>rect").attr("width",e[1][0]-e[0][0])}function j(a){a.select(".extent").attr("y",e[0][1]),a.selectAll(".extent,.e>rect,.w>rect").attr("height",e[1][1]-e[0][1])}function k(){function x(){var a=d3.event.changedTouches;return a?d3.touches(d,a)[0]:d3.mouse(d)}function y(){d3.event.keyCode==32&&(q||(r=null,s[0]-=e[1][0],s[1]-=e[1][1],q=2),Y())}function z(){d3.event.keyCode==32&&q==2&&(s[0]+=e[1][0],s[1]+=e[1][1],q=0,Y())}function A(){var a=x(),d=!1;t&&(a[0]+=t[0],a[1]+=t[1]),q||(d3.event.altKey?(r||(r=[(e[0][0]+e[1][0])/2,(e[0][1]+e[1][1])/2]),s[0]=e[+(a[0]<r[0])][0],s[1]=e[+(a[1]<r[1])][1]):r=null),o&&B(a,b,0)&&(i(m),d=!0),p&&B(a,c,1)&&(j(m),d=!0),d&&(h(m),l({type:"brush",mode:q?"move":"resize"}))}function B(a,b,c){var d=cj(b),g=d[0],h=d[1],i=s[c],j=e[1][c]-e[0][c],k,l;q&&(g-=i,h-=j+i),k=Math.max(g,Math.min(h,a[c])),q?l=(k+=i)+j:(r&&(i=Math.max(g,Math.min(h,2*r[c]-k))),i<k?(l=k,k=i):l=i);if(e[0][c]!==k||e[1][c]!==l)return f=null,e[0][c]=k,e[1][c]=l,!0}function C(){A(),m.style("pointer-events","all").selectAll(".resize").style("display",g.empty()?"none":null),d3.select("body").style("cursor",null),u.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),l({type:"brushend"}),Y()}var d=this,k=d3.select(d3.event.target),l=a.of(d,arguments),m=d3.select(d),n=k.datum(),o=!/^(n|s)$/.test(n)&&b,p=!/^(e|w)$/.test(n)&&c,q=k.classed("extent"),r,s=x(),t,u=d3.select(window).on("mousemove.brush",A).on("mouseup.brush",C).on("touchmove.brush",A).on("touchend.brush",C).on("keydown.brush",y).on("keyup.brush",z);if(q)s[0]=e[0][0]-s[0],s[1]=e[0][1]-s[1];else if(n){var v=+/w$/.test(n),w=+/^n/.test(n);t=[e[1-v][0]-s[0],e[1-w][1]-s[1]],s[0]=e[v][0],s[1]=e[w][1]}else d3.event.altKey&&(r=s.slice());m.style("pointer-events","none").selectAll(".resize").style("display",null),d3.select("body").style("cursor",k.style("cursor")),l({type:"brushstart"}),A(),Y()}var a=$(g,"brushstart","brush","brushend"),b=null,c=null,d=dH[0],e=[[0,0],[0,0]],f;return g.x=function(a){return arguments.length?(b=a,d=dH[!b<<1|!c],g):b},g.y=function(a){return arguments.length?(c=a,d=dH[!b<<1|!c],g):c},g.extent=function(a){var d,h,i,j,k;return arguments.length?(f=[[0,0],[0,0]],b&&(d=a[0],h=a[1],c&&(d=d[0],h=h[0]),f[0][0]=d,f[1][0]=h,b.invert&&(d=b(d),h=b(h)),h<d&&(k=d,d=h,h=k),e[0][0]=d|0,e[1][0]=h|0),c&&(i=a[0],j=a[1],b&&(i=i[1],j=j[1]),f[0][1]=i,f[1][1]=j,c.invert&&(i=c(i),j=c(j)),j<i&&(k=i,i=j,j=k),e[0][1]=i|0,e[1][1]=j|0),g):(a=f||e,b&&(d=a[0][0],h=a[1][0],f||(d=e[0][0],h=e[1][0],b.invert&&(d=b.invert(d),h=b.invert(h)),h<d&&(k=d,d=h,h=k))),c&&(i=a[0][1],j=a[1][1],f||(i=e[0][1],j=e[1][1],c.invert&&(i=c.invert(i),j=c.invert(j)),j<i&&(k=i,i=j,j=k))),b&&c?[[d,i],[h,j]]:b?[d,h]:c&&[i,j])},g.clear=function(){return f=null,e[0][0]=e[0][1]=e[1][0]=e[1][1]=0,g},g.empty=function(){return b&&e[0][0]===e[1][0]||c&&e[0][1]===e[1][1]},d3.rebind(g,a,"on")};var dG={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},dH=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]];d3.behavior={},d3.behavior.drag=function(){function c(){this.on("mousedown.drag",d).on("touchstart.drag",d)}function d(){function j(){var a=c.parentNode,b=d3.event.changedTouches;return b?d3.touches(a,b)[0]:d3.mouse(a)}function k(){if(!c.parentNode)return l();var a=j(),b=a[0]-g[0],e=a[1]-g[1];h|=b|e,g=a,Y(),d({type:"drag",x:a[0]+f[0],y:a[1]+f[1],dx:b,dy:e})}function l(){d({type:"dragend"}),h&&(Y(),d3.event.target===e&&i.on("click.drag",m,!0)),i.on("mousemove.drag",null).on("touchmove.drag",null).on("mouseup.drag",null).on("touchend.drag",null)}function m(){Y(),i.on("click.drag",null)}var c=this,d=a.of(c,arguments),e=d3.event.target,f,g=j(),h=0,i=d3.select(window).on("mousemove.drag",k).on("touchmove.drag",k).on("mouseup.drag",l,!0).on("touchend.drag",l,!0);b?(f=b.apply(c,arguments),f=[f.x-g[0],f.y-g[1]]):f=[0,0],Y(),d({type:"dragstart"})}var a=$(c,"drag","dragstart","dragend"),b=null;return c.origin=function(a){return arguments.length?(b=a,c):b},d3.rebind(c,a,"on")},d3.behavior.zoom=function(){function l(){this.on("mousedown.zoom",r).on("mousewheel.zoom",s).on("mousemove.zoom",t).on("DOMMouseScroll.zoom",s).on("dblclick.zoom",u).on("touchstart.zoom",v).on("touchmove.zoom",w).on("touchend.zoom",v)}function m(b){return[(b[0]-a[0])/c,(b[1]-a[1])/c]}function n(b){return[b[0]*c+a[0],b[1]*c+a[1]]}function o(a){c=Math.max(e[0],Math.min(e[1],a))}function p(b,c){c=n(c),a[0]+=b[0]-c[0],a[1]+=b[1]-c[1]}function q(b){h&&h.domain(g.range().map(function(b){return(b-a[0])/c}).map(g.invert)),j&&j.domain(i.range().map(function(b){return(b-a[1])/c}).map(i.invert)),d3.event.preventDefault(),b({type:"zoom",scale:c,translate:a})}function r(){function h(){d=1,p(d3.mouse(a),g),q(b)}function i(){d&&Y(),e.on("mousemove.zoom",null).on("mouseup.zoom",null),d&&d3.event.target===c&&e.on("click.zoom",j,!0)}function j(){Y(),e.on("click.zoom",null)}var a=this,b=f.of(a,arguments),c=d3.event.target,d=0,e=d3.select(window).on("mousemove.zoom",h).on("mouseup.zoom",i),g=m(d3.mouse(a));window.focus(),Y()}function s(){b||(b=m(d3.mouse(this))),o(Math.pow(2,dK()*.002)*c),p(d3.mouse(this),b),q(f.of(this,arguments))}function t(){b=null}function u(){var a=d3.mouse(this),b=m(a);o(d3.event.shiftKey?c/2:c*2),p(a,b),q(f.of(this,arguments))}function v(){var a=d3.touches(this),e=Date.now();d=c,b={},a.forEach(function(a){b[a.identifier]=m(a)}),Y();if(a.length===1){if(e-k<500){var g=a[0],h=m(a[0]);o(c*2),p(g,h),q(f.of(this,arguments))}k=e}}function w(){var a=d3.touches(this),c=a[0],e=b[c.identifier];if(g=a[1]){var g,h=b[g.identifier];c=[(c[0]+g[0])/2,(c[1]+g[1])/2],e=[(e[0]+h[0])/2,(e[1]+h[1])/2],o(d3.event.scale*d)}p(c,e),k=null,q(f.of(this,arguments))}var a=[0,0],b,c=1,d,e=dJ,f=$(l,"zoom"),g,h,i,j,k;return l.translate=function(b){return arguments.length?(a=b.map(Number),l):a},l.scale=function(a){return arguments.length?(c=+a,l):c},l.scaleExtent=function(a){return arguments.length?(e=a==null?dJ:a.map(Number),l):e},l.x=function(a){return arguments.length?(h=a,g=a.copy(),l):h},l.y=function(a){return arguments.length?(j=a,i=a.copy(),l):j},d3.rebind(l,f,"on")};var dI,dJ=[0,Infinity];d3.layout={},d3.layout.bundle=function(){return function(a){var b=[],c=-1,d=a.length;while(++c<d)b.push(dL(a[c]));return b}},d3.layout.chord=function(){function j(){var a={},j=[],l=d3.range(e),m=[],n,o,p,q,r;b=[],c=[],n=0,q=-1;while(++q<e){o=0,r=-1;while(++r<e)o+=d[q][r];j.push(o),m.push(d3.range(e)),n+=o}g&&l.sort(function(a,b){return g(j[a],j[b])}),h&&m.forEach(function(a,b){a.sort(function(a,c){return h(d[b][a],d[b][c])})}),n=(2*Math.PI-f*e)/n,o=0,q=-1;while(++q<e){p=o,r=-1;while(++r<e){var s=l[q],t=m[s][r],u=d[s][t],v=o,w=o+=u*n;a[s+"-"+t]={index:s,subindex:t,startAngle:v,endAngle:w,value:u}}c[s]={index:s,startAngle:p,endAngle:o,value:(o-p)/n},o+=f}q=-1;while(++q<e){r=q-1;while(++r<e){var x=a[q+"-"+r],y=a[r+"-"+q];(x.value||y.value)&&b.push(x.value<y.value?{source:y,target:x}:{source:x,target:y})}}i&&k()}function k(){b.sort(function(a,b){return i((a.source.value+a.target.value)/2,(b.source.value+b.target.value)/2)})}var a={},b,c,d,e,f=0,g,h,i;return a.matrix=function(f){return arguments.length?(e=(d=f)&&d.length,b=c=null,a):d},a.padding=function(d){return arguments.length?(f=d,b=c=null,a):f},a.sortGroups=function(d){return arguments.length?(g=d,b=c=null,a):g},a.sortSubgroups=function(c){return arguments.length?(h=c,b=null,a):h},a.sortChords=function(c){return arguments.length?(i=c,b&&k(),a):i},a.chords=function(){return b||j(),b},a.groups=function(){return c||j(),c},a},d3.layout.force=function(){function t(a){return function(b,c,d,e,f){if(b.point!==a){var g=b.cx-a.x,h=b.cy-a.y,i=1/Math.sqrt(g*g+h*h);if((e-c)*i<k){var j=b.charge*i*i;return a.px-=g*j,a.py-=h*j,!0}if(b.point&&isFinite(i)){var j=b.pointCharge*i*i;a.px-=g*j,a.py-=h*j}}return!b.charge}}function u(b){dQ(dP=b),dO=a}var a={},b=d3.dispatch("start","tick","end"),c=[1,1],d,e,f=.9,g=dV,h=dW,i=-30,j=.1,k=.8,l,n=[],o=[],q,r,s;return a.tick=function(){if((e*=.99)<.005)return b.end({type:"end",alpha:e=0}),!0;var a=n.length,d=o.length,g,h,k,l,m,p,u,v,w;for(h=0;h<d;++h){k=o[h],l=k.source,m=k.target,v=m.x-l.x,w=m.y-l.y;if(p=v*v+w*w)p=e*r[h]*((p=Math.sqrt(p))-q[h])/p,v*=p,w*=p,m.x-=v*(u=l.weight/(m.weight+l.weight)),m.y-=w*u,l.x+=v*(u=1-u),l.y+=w*u}if(u=e*j){v=c[0]/2,w=c[1]/2,h=-1;if(u)while(++h<a)k=n[h],k.x+=(v-k.x)*u,k.y+=(w-k.y)*u}if(i){dU(g=d3.geom.quadtree(n),e,s),h=-1;while(++h<a)(k=n[h]).fixed||g.visit(t(k))}h=-1;while(++h<a)k=n[h],k.fixed?(k.x=k.px,k.y=k.py):(k.x-=(k.px-(k.px=k.x))*f,k.y-=(k.py-(k.py=k.y))*f);b.tick({type:"tick",alpha:e})},a.nodes=function(b){return arguments.length?(n=b,a):n},a.links=function(b){return arguments.length?(o=b,a):o},a.size=function(b){return arguments.length?(c=b,a):c},a.linkDistance=function(b){return arguments.length?(g=p(b),a):g},a.distance=a.linkDistance,a.linkStrength=function(b){return arguments.length?(h=p(b),a):h},a.friction=function(b){return arguments.length?(f=b,a):f},a.charge=function(b){return arguments.length?(i=typeof b=="function"?b:+b,a):i},a.gravity=function(b){return arguments.length?(j=b,a):j},a.theta=function(b){return arguments.length?(k=b,a):k},a.alpha=function(c){return arguments.length?(e?c>0?e=c:e=0:c>0&&(b.start({type:"start",alpha:e=c}),d3.timer(a.tick)),a):e},a.start=function(){function p(a,c){var d=t(b),e=-1,f=d.length,g;while(++e<f)if(!isNaN(g=d[e][a]))return g;return Math.random()*c}function t(){if(!l){l=[];for(d=0;d<e;++d)l[d]=[];for(d=0;d<f;++d){var a=o[d];l[a.source.index].push(a.target),l[a.target.index].push(a.source)}}return l[b]}var b,d,e=n.length,f=o.length,j=c[0],k=c[1],l,m;for(b=0;b<e;++b)(m=n[b]).index=b,m.weight=0;q=[],r=[];for(b=0;b<f;++b)m=o[b],typeof m.source=="number"&&(m.source=n[m.source]),typeof m.target=="number"&&(m.target=n[m.target]),q[b]=g.call(this,m,b),r[b]=h.call(this,m,b),++m.source.weight,++m.target.weight;for(b=0;b<e;++b)m=n[b],isNaN(m.x)&&(m.x=p("x",j)),isNaN(m.y)&&(m.y=p("y",k)),isNaN(m.px)&&(m.px=m.x),isNaN(m.py)&&(m.py=m.y);s=[];if(typeof i=="function")for(b=0;b<e;++b)s[b]=+i.call(this,n[b],b);else for(b=0;b<e;++b)s[b]=i;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){d||(d=d3.behavior.drag().origin(m).on("dragstart",u).on("drag",dT).on("dragend",dS)),this.on("mouseover.force",dQ).on("mouseout.force",dR).call(d)},d3.rebind(a,b,"on")};var dO,dP;d3.layout.partition=function(){function c(a,b,d,e){var f=a.children;a.x=b,a.y=a.depth*e,a.dx=d,a.dy=e;if(f&&(h=f.length)){var g=-1,h,i,j;d=a.value?d/a.value:0;while(++g<h)c(i=f[g],b,j=i.value*d,e),b+=j}}function d(a){var b=a.children,c=0;if(b&&(f=b.length)){var e=-1,f;while(++e<f)c=Math.max(c,d(b[e]))}return 1+c}function e(e,f){var g=a.call(this,e,f);return c(g[0],0,b[0],b[1]/d(g[0])),g}var a=d3.layout.hierarchy(),b=[1,1];return e.size=function(a){return arguments.length?(b=a,e):b},ej(e,a)},d3.layout.pie=function(){function e(f,g){var h=f.map(function(b,c){return+a.call(e,b,c)}),i=+(typeof c=="function"?c.apply(this,arguments):c),j=((typeof d=="function"?d.apply(this,arguments):d)-c)/d3.sum(h),k=d3.range(f.length);b!=null&&k.sort(b===dX?function(a,b){return h[b]-h[a]}:function(a,c){return b(f[a],f[c])});var l=[];return k.forEach(function(a){var b;l[a]={data:f[a],value:b=h[a],startAngle:i,endAngle:i+=b*j}}),l}var a=Number,b=dX,c=0,d=2*Math.PI;return e.value=function(b){return arguments.length?(a=b,e):a},e.sort=function(a){return arguments.length?(b=a,e):b},e.startAngle=function(a){return arguments.length?(c=a,e):c},e.endAngle=function(a){return arguments.length?(d=a,e):d},e};var dX={};d3.layout.stack=function(){function g(h,i){var j=h.map(function(b,c){return a.call(g,b,c)}),k=j.map(function(a,b){return a.map(function(a,b){return[e.call(g,a,b),f.call(g,a,b)]})}),l=b.call(g,k,i);j=d3.permute(j,l),k=d3.permute(k,l);var m=c.call(g,k,i),n=j.length,o=j[0].length,p,q,r;for(q=0;q<o;++q){d.call(g,j[0][q],r=m[q],k[0][q][1]);for(p=1;p<n;++p)d.call(g,j[p][q],r+=k[p-1][q][1],k[p][q][1])}return h}var a=m,b=eb,c=ec,d=d$,e=dY,f=dZ;return g.values=function(b){return arguments.length?(a=b,g):a},g.order=function(a){return arguments.length?(b=typeof a=="function"?a:d_.get(a)||eb,g):b},g.offset=function(a){return arguments.length?(c=typeof a=="function"?a:ea.get(a)||ec,g):c},g.x=function(a){return arguments.length?(e=a,g):e},g.y=function(a){return arguments.length?(f=a,g):f},g.out=function(a){return arguments.length?(d=a,g):d},g};var d_=d3.map({"inside-out":function(a){var b=a.length,c,d,e=a.map(ed),f=a.map(ee),g=d3.range(b).sort(function(a,b){return e[a]-e[b]}),h=0,i=0,j=[],k=[];for(c=0;c<b;++c)d=g[c],h<i?(h+=f[d],j.push(d)):(i+=f[d],k.push(d));return k.reverse().concat(j)},reverse:function(a){return d3.range(a.length).reverse()},"default":eb}),ea=d3.map({silhouette:function(a){var b=a.length,c=a[0].length,d=[],e=0,f,g,h,i=[];for(g=0;g<c;++g){for(f=0,h=0;f<b;f++)h+=a[f][g][1];h>e&&(e=h),d.push(h)}for(g=0;g<c;++g)i[g]=(e-d[g])/2;return i},wiggle:function(a){var b=a.length,c=a[0],d=c.length,e=0,f,g,h,i,j,k,l,m,n,o=[];o[0]=m=n=0;for(g=1;g<d;++g){for(f=0,i=0;f<b;++f)i+=a[f][g][1];for(f=0,j=0,l=c[g][0]-c[g-1][0];f<b;++f){for(h=0,k=(a[f][g][1]-a[f][g-1][1])/(2*l);h<f;++h)k+=(a[h][g][1]-a[h][g-1][1])/l;j+=k*a[f][g][1]}o[g]=m-=i?j/i*l:0,m<n&&(n=m)}for(g=0;g<d;++g)o[g]-=n;return o},expand:function(a){var b=a.length,c=a[0].length,d=1/b,e,f,g,h=[];for(f=0;f<c;++f){for(e=0,g=0;e<b;e++)g+=a[e][f][1];if(g)for(e=0;e<b;e++)a[e][f][1]/=g;else for(e=0;e<b;e++)a[e][f][1]=d}for(f=0;f<c;++f)h[f]=0;return h},zero:ec});d3.layout.histogram=function(){function e(e,f){var g=[],h=e.map(b,this),i=c.call(this,h,f),j=d.call(this,i,h,f),k,f=-1,l=h.length,m=j.length-1,n=a?1:1/l,o;while(++f<m)k=g[f]=[],k.dx=j[f+1]-(k.x=j[f]),k.y=0;if(m>0){f=-1;while(++f<l)o=h[f],o>=i[0]&&o<=i[1]&&(k=g[d3.bisect(j,o,1,m)-1],k.y+=n,k.push(e[f]))}return g}var a=!0,b=Number,c=ei,d=eg;return e.value=function(a){return arguments.length?(b=a,e):b},e.range=function(a){return arguments.length?(c=p(a),e):c},e.bins=function(a){return arguments.length?(d=typeof a=="number"?function(b){return eh(b,a)}:p(a),e):d},e.frequency=function(b){return arguments.length?(a=!!b,e):a},e},d3.layout.hierarchy=function(){function d(e,g,h){var i=b.call(f,e,g),j=eo?e:{data:e};j.depth=g,h.push(j);if(i&&(l=i.length)){var k=-1,l,m=j.children=[],n=0,o=g+1,p;while(++k<l)p=d(i[k],o,h),p.parent=j,m.push(p),n+=p.value;a&&m.sort(a),c&&(j.value=n)}else c&&(j.value=+c.call(f,e,g)||0);return j}function e(a,b){var d=a.children,g=0;if(d&&(i=d.length)){var h=-1,i,j=b+1;while(++h<i)g+=e(d[h],j)}else c&&(g=+c.call(f,eo?a:a.data,b)||0);return c&&(a.value=g),g}function f(a){var b=[];return d(a,0,b),b}var a=em,b=ek,c=el;return f.sort=function(b){return arguments.length?(a=b,f):a},f.children=function(a){return arguments.length?(b=a,f):b},f.value=function(a){return arguments.length?(c=a,f):c},f.revalue=function(a){return e(a,0),a},f};var eo=!1;d3.layout.pack=function(){function c(c,d){var e=a.call(this,c,d),f=e[0];f.x=0,f.y=0,ew(f);var g=b[0],h=b[1],i=1/Math.max(2*f.r/g,2*f.r/h);return ex(f,g/2,h/2,i),e}var a=d3.layout.hierarchy().sort(ep),b=[1,1];return c.size=function(a){return arguments.length?(b=a,c):b},ej(c,a)},d3.layout.cluster=function(){function d(d,e){var f=a.call(this,d,e),g=f[0],h,i=0,j,k;eK(g,function(a){var c=a.children;c&&c.length?(a.x=eA(c),a.y=ez(c)):(a.x=h?i+=b(a,h):0,a.y=0,h=a)});var l=eB(g),m=eC(g),n=l.x-b(l,m)/2,o=m.x+b(m,l)/2;return eK(g,function(a){a.x=(a.x-n)/(o-n)*c[0],a.y=(1-(g.y?a.y/g.y:1))*c[1]}),f}var a=d3.layout.hierarchy().sort(null).value(null),b=eD,c=[1,1];return d.separation=function(a){return arguments.length?(b=a,d):b},d.size=function(a){return arguments.length?(c=a,d):c},ej(d,a)},d3.layout.tree=function(){function d(d,e){function h(a,c){var d=a.children,e=a._tree;if(d&&(f=d.length)){var f,g=d[0],i,k=g,l,m=-1;while(++m<f)l=d[m],h(l,i),k=j(l,i,k),i=l;eL(a);var n=.5*(g._tree.prelim+l._tree.prelim);c?(e.prelim=c._tree.prelim+b(a,c),e.mod=e.prelim-n):e.prelim=n}else c&&(e.prelim=c._tree.prelim+b(a,c))}function i(a,b){a.x=a._tree.prelim+b;var c=a.children;if(c&&(e=c.length)){var d=-1,e;b+=a._tree.mod;while(++d<e)i(c[d],b)}}function j(a,c,d){if(c){var e=a,f=a,g=c,h=a.parent.children[0],i=e._tree.mod,j=f._tree.mod,k=g._tree.mod,l=h._tree.mod,m;while(g=eF(g),e=eE(e),g&&e)h=eE(h),f=eF(f),f._tree.ancestor=a,m=g._tree.prelim+k-e._tree.prelim-i+b(g,e),m>0&&(eM(eN(g,a,d),a,m),i+=m,j+=m),k+=g._tree.mod,i+=e._tree.mod,l+=h._tree.mod,j+=f._tree.mod;g&&!eF(f)&&(f._tree.thread=g,f._tree.mod+=k-j),e&&!eE(h)&&(h._tree.thread=e,h._tree.mod+=i-l,d=a)}return d}var f=a.call(this,d,e),g=f[0];eK(g,function(a,b){a._tree={ancestor:a,prelim:0,mod:0,change:0,shift:0,number:b?b._tree.number+1:0}}),h(g),i(g,-g._tree.prelim);var k=eG(g,eI),l=eG(g,eH),m=eG(g,eJ),n=k.x-b(k,l)/2,o=l.x+b(l,k)/2,p=m.depth||1;return eK(g,function(a){a.x=(a.x-n)/(o-n)*c[0],a.y=a.depth/p*c[1],delete a._tree}),f}var a=d3.layout.hierarchy().sort(null).value(null),b=eD,c=[1,1];return d.separation=function(a){return arguments.length?(b=a,d):b},d.size=function(a){return arguments.length?(c=a,d):c},ej(d,a)},d3.layout.treemap=function(){function i(a,b){var c=-1,d=a.length,e,f;while(++c<d)f=(e=a[c]).value*(b<0?0:b),e.area=isNaN(f)||f<=0?0:f}function j(a){var b=a.children;if(b&&b.length){var c=e(a),d=[],f=b.slice(),g,h=Infinity,k,n=Math.min(c.dx,c.dy),o;i(f,c.dx*c.dy/a.value),d.area=0;while((o=f.length)>0)d.push(g=f[o-1]),d.area+=g.area,(k=l(d,n))<=h?(f.pop(),h=k):(d.area-=d.pop().area,m(d,n,c,!1),n=Math.min(c.dx,c.dy),d.length=d.area=0,h=Infinity);d.length&&(m(d,n,c,!0),d.length=d.area=0),b.forEach(j)}}function k(a){var b=a.children;if(b&&b.length){var c=e(a),d=b.slice(),f,g=[];i(d,c.dx*c.dy/a.value),g.area=0;while(f=d.pop())g.push(f),g.area+=f.area,f.z!=null&&(m(g,f.z?c.dx:c.dy,c,!d.length),g.length=g.area=0);b.forEach(k)}}function l(a,b){var c=a.area,d,e=0,f=Infinity,g=-1,i=a.length;while(++g<i){if(!(d=a[g].area))continue;d<f&&(f=d),d>e&&(e=d)}return c*=c,b*=b,c?Math.max(b*e*h/c,c/(b*f*h)):Infinity}function m(a,c,d,e){var f=-1,g=a.length,h=d.x,i=d.y,j=c?b(a.area/c):0,k;if(c==d.dx){if(e||j>d.dy)j=d.dy;while(++f<g)k=a[f],k.x=h,k.y=i,k.dy=j,h+=k.dx=Math.min(d.x+d.dx-h,j?b(k.area/j):0);k.z=!0,k.dx+=d.x+d.dx-h,d.y+=j,d.dy-=j}else{if(e||j>d.dx)j=d.dx;while(++f<g)k=a[f],k.x=h,k.y=i,k.dx=j,i+=k.dy=Math.min(d.y+d.dy-i,j?b(k.area/j):0);k.z=!1,k.dy+=d.y+d.dy-i,d.x+=j,d.dx-=j}}function n(b){var d=g||a(b),e=d[0];return e.x=0,e.y=0,e.dx=c[0],e.dy=c[1],g&&a.revalue(e),i([e],e.dx*e.dy/e.value),(g?k:j)(e),f&&(g=d),d}var a=d3.layout.hierarchy(),b=Math.round,c=[1,1],d=null,e=eO,f=!1,g,h=.5*(1+Math.sqrt(5));return n.size=function(a){return arguments.length?(c=a,n):c},n.padding=function(a){function b(b){var c=a.call(n,b,b.depth);return c==null?eO(b):eP(b,typeof c=="number"?[c,c,c,c]:c)}function c(b){return eP(b,a)}if(!arguments.length)return d;var f;return e=(d=a)==null?eO:(f=typeof a)==="function"?b:f==="number"?(a=[a,a,a,a],c):c,n},n.round=function(a){return arguments.length?(b=a?Math.round:Number,n):b!=Number},n.sticky=function(a){return arguments.length?(f=a,g=null,n):f},n.ratio=function(a){return arguments.length?(h=a,n):h},ej(n,a)},d3.csv=function(a,b){d3.text(a,"text/csv",function(a){b(a&&d3.csv.parse(a))})},d3.csv.parse=function(a){var b;return d3.csv.parseRows(a,function(a,c){if(c){var d={},e=-1,f=b.length;while(++e<f)d[b[e]]=a[e];return d}return b=a,null})},d3.csv.parseRows=function(a,b){function j(){if(f.lastIndex>=a.length)return d;if(i)return i=!1,c;var b=f.lastIndex;if(a.charCodeAt(b)===34){var e=b;while(e++<a.length)if(a.charCodeAt(e)===34){if(a.charCodeAt(e+1)!==34)break;e++}f.lastIndex=e+2;var g=a.charCodeAt(e+1);return g===13?(i=!0,a.charCodeAt(e+2)===10&&f.lastIndex++):g===10&&(i=!0),a.substring(b+1,e).replace(/""/g,'"')}var h=f.exec(a);return h?(i=h[0].charCodeAt(0)!==44,a.substring(b,h.index)):(f.lastIndex=a.length,a.substring(b))}var c={},d={},e=[],f=/\r\n|[,\r\n]/g,g=0,h,i;f.lastIndex=0;while((h=j())!==d){var k=[];while(h!==c&&h!==d)k.push(h),h=j();if(b&&!(k=b(k,g++)))continue;e.push(k)}return e},d3.csv.format=function(a){return a.map(eQ).join("\n")},d3.geo={};var eS=Math.PI/180;d3.geo.azimuthal=function(){function i(b){var f=b[0]*eS-e,i=b[1]*eS,j=Math.cos(f),k=Math.sin(f),l=Math.cos(i),m=Math.sin(i),n=a!=="orthographic"?h*m+g*l*j:null,o,p=a==="stereographic"?1/(1+n):a==="gnomonic"?1/n:a==="equidistant"?(o=Math.acos(n),o?o/Math.sin(o):0):a==="equalarea"?Math.sqrt(2/(1+n)):1,q=p*l*k,r=p*(h*l*j-g*m);return[c*q+d[0],c*r+d[1]]}var a="orthographic",b,c=200,d=[480,250],e,f,g,h;return i.invert=function(b){var f=(b[0]-d[0])/c,i=(b[1]-d[1])/c,j=Math.sqrt(f*f+i*i),k=a==="stereographic"?2*Math.atan(j):a==="gnomonic"?Math.atan(j):a==="equidistant"?j:a==="equalarea"?2*Math.asin(.5*j):Math.asin(j),l=Math.sin(k),m=Math.cos(k);return[(e+Math.atan2(f*l,j*g*m+i*h*l))/eS,Math.asin(m*h-(j?i*l*g/j:0))/eS]},i.mode=function(b){return arguments.length?(a=b+"",i):a},i.origin=function(a){return arguments.length?(b=a,e=b[0]*eS,f=b[1]*eS,g=Math.cos(f),h=Math.sin(f),i):b},i.scale=function(a){return arguments.length?(c=+a,i):c},i.translate=function(a){return arguments.length?(d=[+a[0],+a[1]],i):d},i.origin([0,0])},d3.geo.albers=function(){function i(a){var b=f*(eS*a[0]-e),i=Math.sqrt(g-2*f*Math.sin(eS*a[1]))/f;return[c*i*Math.sin(b)+d[0],c*(i*Math.cos(b)-h)+d[1]]}function j(){var c=eS*b[0],d=eS*b[1],j=eS*a[1],k=Math.sin(c),l=Math.cos(c);return e=eS*a[0],f=.5*(k+Math.sin(d)),g=l*l+2*f*k,h=Math.sqrt(g-2*f*Math.sin(j))/f,i}var a=[-98,38],b=[29.5,45.5],c=1e3,d=[480,250],e,f,g,h;return i.invert=function(a){var b=(a[0]-d[0])/c,i=(a[1]-d[1])/c,j=h+i,k=Math.atan2(b,j),l=Math.sqrt(b*b+j*j);return[(e+k/f)/eS,Math.asin((g-l*l*f*f)/(2*f))/eS]},i.origin=function(b){return arguments.length?(a=[+b[0],+b[1]],j()):a},i.parallels=function(a){return arguments.length?(b=[+a[0],+a[1]],j()):b},i.scale=function(a){return arguments.length?(c=+a,i):c},i.translate=function(a){return arguments.length?(d=[+a[0],+a[1]],i):d},j()},d3.geo.albersUsa=function(){function e(e){var f=e[0],g=e[1];return(g>50?b:f<-140?c:g<21?d:a)(e)}var a=d3.geo.albers(),b=d3.geo.albers().origin([-160,60]).parallels([55,65]),c=d3.geo.albers().origin([-160,20]).parallels([8,18]),d=d3.geo.albers().origin([-60,10]).parallels([8,18]);return e.scale=function(f){return arguments.length?(a.scale(f),b.scale(f*.6),c.scale(f),d.scale(f*1.5),e.translate(a.translate())):a.scale()},e.translate=function(f){if(!arguments.length)return a.translate();var g=a.scale()/1e3,h=f[0],i=f[1];return a.translate(f),b.translate([h-400*g,i+170*g]),c.translate([h-190*g,i+200*g]),d.translate([h+580*g,i+430*g]),e},e.scale(a.scale())},d3.geo.bonne=function(){function g(g){var h=g[0]*eS-c,i=g[1]*eS-d;if(e){var j=f+e-i,k=h*Math.cos(i)/j;h=j*Math.sin(k),i=j*Math.cos(k)-f}else h*=Math.cos(i),i*=-1;return[a*h+b[0],a*i+b[1]]}var a=200,b=[480,250],c,d,e,f;return g.invert=function(d){var g=(d[0]-b[0])/a,h=(d[1]-b[1])/a;if(e){var i=f+h,j=Math.sqrt(g*g+i*i);h=f+e-j,g=c+j*Math.atan2(g,i)/Math.cos(h)}else h*=-1,g/=Math.cos(h);return[g/eS,h/eS]},g.parallel=function(a){return arguments.length?(f=1/Math.tan(e=a*eS),g):e/eS},g.origin=function(a){return arguments.length?(c=a[0]*eS,d=a[1]*eS,g):[c/eS,d/eS]},g.scale=function(b){return arguments.length?(a=+b,g):a},g.translate=function(a){return arguments.length?(b=[+a[0],+a[1]],g):b},g.origin([0,0]).parallel(45)},d3.geo.equirectangular=function(){function c(c){var d=c[0]/360,e=-c[1]/360;return[a*d+b[0],a*e+b[1]]}var a=500,b=[480,250];return c.invert=function(c){var d=(c[0]-b[0])/a,e=(c[1]-b[1])/a;return[360*d,-360*e]},c.scale=function(b){return arguments.length?(a=+b,c):a},c.translate=function(a){return arguments.length?(b=[+a[0],+a[1]],c):b},c},d3.geo.mercator=function(){function c(c){var d=c[0]/360,e=-(Math.log(Math.tan(Math.PI/4+c[1]*eS/2))/eS)/360;return[a*d+b[0],a*Math.max(-0.5,Math.min(.5,e))+b[1]]}var a=500,b=[480,250];return c.invert=function(c){var d=(c[0]-b[0])/a,e=(c[1]-b[1])/a;return[360*d,2*Math.atan(Math.exp(-360*e*eS))/eS-90]},c.scale=function(b){return arguments.length?(a=+b,c):a},c.translate=function(a){return arguments.length?(b=[+a[0],+a[1]],c):b},c},d3.geo.path=function(){function e(c,e){typeof a=="function"&&(b=eU(a.apply(this,arguments))),g(c);var f=d.length?d.join(""):null;return d=[],f}function f(a){return c(a).join(",")}function i(a){var b=l(a[0]),c=0,d=a.length;while(++c<d)b-=l(a[c]);return b}function j(a){var b=d3.geom.polygon(a[0].map(c)),d=b.area(),e=b.centroid(d<0?(d*=-1,1):-1),f=e[0],g=e[1],h=d,i=0,j=a.length;while(++i<j)b=d3.geom.polygon(a[i].map(c)),d=b.area(),e=b.centroid(d<0?(d*=-1,1):-1),f-=e[0],g-=e[1],h-=d;return[f,g,6*h]}function l(a){return Math.abs(d3.geom.polygon(a.map(c)).area())}var a=4.5,b=eU(a),c=d3.geo.albersUsa(),d=[],g=eT({FeatureCollection:function(a){var b=a.features,c=-1,e=b.length;while(++c<e)d.push(g(b[c].geometry))},Feature:function(a){g(a.geometry)},Point:function(a){d.push("M",f(a.coordinates),b)},MultiPoint:function(a){var c=a.coordinates,e=-1,g=c.length;while(++e<g)d.push("M",f(c[e]),b)},LineString:function(a){var b=a.coordinates,c=-1,e=b.length;d.push("M");while(++c<e)d.push(f(b[c]),"L");d.pop()},MultiLineString:function(a){var b=a.coordinates,c=-1,e=b.length,g,h,i;while(++c<e){g=b[c],h=-1,i=g.length,d.push("M");while(++h<i)d.push(f(g[h]),"L");d.pop()}},Polygon:function(a){var b=a.coordinates,c=-1,e=b.length,g,h,i;while(++c<e){g=b[c],h=-1;if((i=g.length-1)>0){d.push("M");while(++h<i)d.push(f(g[h]),"L");d[d.length-1]="Z"}}},MultiPolygon:function(a){var b=a.coordinates,c=-1,e=b.length,g,h,i,j,k,l;while(++c<e){g=b[c],h=-1,i=g.length;while(++h<i){j=g[h],k=-1;if((l=j.length-1)>0){d.push("M");while(++k<l)d.push(f(j[k]),"L");d[d.length-1]="Z"}}}},GeometryCollection:function(a){var b=a.geometries,c=-1,e=b.length;while(++c<e)d.push(g(b[c]))}}),h=e.area=eT({FeatureCollection:function(a){var b=0,c=a.features,d=-1,e=c.length;while(++d<e)b+=h(c[d]);return b},Feature:function(a){return h(a.geometry)},Polygon:function(a){return i(a.coordinates)},MultiPolygon:function(a){var b=0,c=a.coordinates,d=-1,e=c.length;while(++d<e)b+=i(c[d]);return b},GeometryCollection:function(a){var b=0,c=a.geometries,d=-1,e=c.length;while(++d<e)b+=h(c[d]);return b}},0),k=e.centroid=eT({Feature:function(a){return k(a.geometry)},Polygon:function(a){var b=j(a.coordinates);return[b[0]/b[2],b[1]/b[2]]},MultiPolygon:function(a){var b=0,c=a.coordinates,d,e=0,f=0,g=0,h=-1,i=c.length;while(++h<i)d=j(c[h]),e+=d[0],f+=d[1],g+=d[2];return[ |
2562 | +e/g,f/g]}});return e.projection=function(a){return c=a,e},e.pointRadius=function(c){return typeof c=="function"?a=c:(a=+c,b=eU(a)),e},e},d3.geo.bounds=function(a){var b=Infinity,c=Infinity,d=-Infinity,e=-Infinity;return eV(a,function(a,f){a<b&&(b=a),a>d&&(d=a),f<c&&(c=f),f>e&&(e=f)}),[[b,c],[d,e]]};var eW={Feature:eX,FeatureCollection:eY,GeometryCollection:eZ,LineString:e$,MultiLineString:e_,MultiPoint:e$,MultiPolygon:fa,Point:fb,Polygon:fc};d3.geo.circle=function(){function e(){}function f(a){return d.distance(a)<c}function h(a){var b=-1,e=a.length,f=[],g,h,j,k,l;while(++b<e)l=d.distance(j=a[b]),l<c?(h&&f.push(fg(h,j)((k-c)/(k-l))),f.push(j),g=h=null):(h=j,!g&&f.length&&(f.push(fg(f[f.length-1],h)((c-k)/(l-k))),g=h)),k=l;return g=a[0],h=f[0],h&&j[0]===g[0]&&j[1]===g[1]&&(j[0]!==h[0]||j[1]!==h[1])&&f.push(h),i(f)}function i(a){var b=0,c=a.length,e,f,g=c?[a[0]]:a,h,i=d.source();while(++b<c){h=d.source(a[b-1])(a[b]).coordinates;for(e=0,f=h.length;++e<f;)g.push(h[e])}return d.source(i),g}var a=[0,0],b=89.99,c=b*eS,d=d3.geo.greatArc().source(a).target(m);e.clip=function(b){return typeof a=="function"&&d.source(a.apply(this,arguments)),g(b)||null};var g=eT({FeatureCollection:function(a){var b=a.features.map(g).filter(m);return b&&(a=Object.create(a),a.features=b,a)},Feature:function(a){var b=g(a.geometry);return b&&(a=Object.create(a),a.geometry=b,a)},Point:function(a){return f(a.coordinates)&&a},MultiPoint:function(a){var b=a.coordinates.filter(f);return b.length&&{type:a.type,coordinates:b}},LineString:function(a){var b=h(a.coordinates);return b.length&&(a=Object.create(a),a.coordinates=b,a)},MultiLineString:function(a){var b=a.coordinates.map(h).filter(function(a){return a.length});return b.length&&(a=Object.create(a),a.coordinates=b,a)},Polygon:function(a){var b=a.coordinates.map(h);return b[0].length&&(a=Object.create(a),a.coordinates=b,a)},MultiPolygon:function(a){var b=a.coordinates.map(function(a){return a.map(h)}).filter(function(a){return a[0].length});return b.length&&(a=Object.create(a),a.coordinates=b,a)},GeometryCollection:function(a){var b=a.geometries.map(g).filter(m);return b.length&&(a=Object.create(a),a.geometries=b,a)}});return e.origin=function(b){return arguments.length?(a=b,typeof a!="function"&&d.source(a),e):a},e.angle=function(a){return arguments.length?(c=(b=+a)*eS,e):b},d3.rebind(e,d,"precision")},d3.geo.greatArc=function(){function g(){var a=g.distance.apply(this,arguments),c=0,h=e/a,i=[b];while((c+=h)<1)i.push(f(c));return i.push(d),{type:"LineString",coordinates:i}}var a=fd,b,c=fe,d,e=6*eS,f=ff();return g.distance=function(){return typeof a=="function"&&f.source(b=a.apply(this,arguments)),typeof c=="function"&&f.target(d=c.apply(this,arguments)),f.distance()},g.source=function(c){return arguments.length?(a=c,typeof a!="function"&&f.source(b=a),g):a},g.target=function(a){return arguments.length?(c=a,typeof c!="function"&&f.target(d=c),g):c},g.precision=function(a){return arguments.length?(e=a*eS,g):e/eS},g},d3.geo.greatCircle=d3.geo.circle,d3.geom={},d3.geom.contour=function(a,b){var c=b||fj(a),d=[],e=c[0],f=c[1],g=0,h=0,i=NaN,j=NaN,k=0;do k=0,a(e-1,f-1)&&(k+=1),a(e,f-1)&&(k+=2),a(e-1,f)&&(k+=4),a(e,f)&&(k+=8),k===6?(g=j===-1?-1:1,h=0):k===9?(g=0,h=i===1?-1:1):(g=fh[k],h=fi[k]),g!=i&&h!=j&&(d.push([e,f]),i=g,j=h),e+=g,f+=h;while(c[0]!=e||c[1]!=f);return d};var fh=[1,0,1,1,-1,0,-1,1,0,0,0,0,-1,0,-1,NaN],fi=[0,-1,0,0,0,-1,0,0,1,-1,1,1,0,-1,0,NaN];d3.geom.hull=function(a){if(a.length<3)return[];var b=a.length,c=b-1,d=[],e=[],f,g,h=0,i,j,k,l,m,n,o,p;for(f=1;f<b;++f)a[f][1]<a[h][1]?h=f:a[f][1]==a[h][1]&&(h=a[f][0]<a[h][0]?f:h);for(f=0;f<b;++f){if(f===h)continue;j=a[f][1]-a[h][1],i=a[f][0]-a[h][0],d.push({angle:Math.atan2(j,i),index:f})}d.sort(function(a,b){return a.angle-b.angle}),o=d[0].angle,n=d[0].index,m=0;for(f=1;f<c;++f)g=d[f].index,o==d[f].angle?(i=a[n][0]-a[h][0],j=a[n][1]-a[h][1],k=a[g][0]-a[h][0],l=a[g][1]-a[h][1],i*i+j*j>=k*k+l*l?d[f].index=-1:(d[m].index=-1,o=d[f].angle,m=f,n=g)):(o=d[f].angle,m=f,n=g);e.push(h);for(f=0,g=0;f<2;++g)d[g].index!==-1&&(e.push(d[g].index),f++);p=e.length;for(;g<c;++g){if(d[g].index===-1)continue;while(!fk(e[p-2],e[p-1],d[g].index,a))--p;e[p++]=d[g].index}var q=[];for(f=0;f<p;++f)q.push(a[e[f]]);return q},d3.geom.polygon=function(a){return a.area=function(){var b=0,c=a.length,d=a[c-1][0]*a[0][1],e=a[c-1][1]*a[0][0];while(++b<c)d+=a[b-1][0]*a[b][1],e+=a[b-1][1]*a[b][0];return(e-d)*.5},a.centroid=function(b){var c=-1,d=a.length,e=0,f=0,g,h=a[d-1],i;arguments.length||(b=-1/(6*a.area()));while(++c<d)g=h,h=a[c],i=g[0]*h[1]-h[0]*g[1],e+=(g[0]+h[0])*i,f+=(g[1]+h[1])*i;return[e*b,f*b]},a.clip=function(b){var c,d=-1,e=a.length,f,g,h=a[e-1],i,j,k;while(++d<e){c=b.slice(),b.length=0,i=a[d],j=c[(g=c.length)-1],f=-1;while(++f<g)k=c[f],fl(k,h,i)?(fl(j,h,i)||b.push(fm(j,k,h,i)),b.push(k)):fl(j,h,i)&&b.push(fm(j,k,h,i)),j=k;h=i}return b},a},d3.geom.voronoi=function(a){var b=a.map(function(){return[]});return fo(a,function(a){var c,d,e,f,g,h;a.a===1&&a.b>=0?(c=a.ep.r,d=a.ep.l):(c=a.ep.l,d=a.ep.r),a.a===1?(g=c?c.y:-1e6,e=a.c-a.b*g,h=d?d.y:1e6,f=a.c-a.b*h):(e=c?c.x:-1e6,g=a.c-a.a*e,f=d?d.x:1e6,h=a.c-a.a*f);var i=[e,g],j=[f,h];b[a.region.l.index].push(i,j),b[a.region.r.index].push(i,j)}),b.map(function(b,c){var d=a[c][0],e=a[c][1];return b.forEach(function(a){a.angle=Math.atan2(a[0]-d,a[1]-e)}),b.sort(function(a,b){return a.angle-b.angle}).filter(function(a,c){return!c||a.angle-b[c-1].angle>1e-10})})};var fn={l:"r",r:"l"};d3.geom.delaunay=function(a){var b=a.map(function(){return[]}),c=[];return fo(a,function(c){b[c.region.l.index].push(a[c.region.r.index])}),b.forEach(function(b,d){var e=a[d],f=e[0],g=e[1];b.forEach(function(a){a.angle=Math.atan2(a[0]-f,a[1]-g)}),b.sort(function(a,b){return a.angle-b.angle});for(var h=0,i=b.length-1;h<i;h++)c.push([e,b[h],b[h+1]])}),c},d3.geom.quadtree=function(a,b,c,d,e){function k(a,b,c,d,e,f){if(isNaN(b.x)||isNaN(b.y))return;if(a.leaf){var g=a.point;g?Math.abs(g.x-b.x)+Math.abs(g.y-b.y)<.01?l(a,b,c,d,e,f):(a.point=null,l(a,g,c,d,e,f),l(a,b,c,d,e,f)):a.point=b}else l(a,b,c,d,e,f)}function l(a,b,c,d,e,f){var g=(c+e)*.5,h=(d+f)*.5,i=b.x>=g,j=b.y>=h,l=(j<<1)+i;a.leaf=!1,a=a.nodes[l]||(a.nodes[l]=fp()),i?c=g:e=g,j?d=h:f=h,k(a,b,c,d,e,f)}var f,g=-1,h=a.length;h&&isNaN(a[0].x)&&(a=a.map(fr));if(arguments.length<5)if(arguments.length===3)e=d=c,c=b;else{b=c=Infinity,d=e=-Infinity;while(++g<h)f=a[g],f.x<b&&(b=f.x),f.y<c&&(c=f.y),f.x>d&&(d=f.x),f.y>e&&(e=f.y);var i=d-b,j=e-c;i>j?e=c+i:d=b+j}var m=fp();return m.add=function(a){k(m,a,b,c,d,e)},m.visit=function(a){fq(a,m,b,c,d,e)},a.forEach(m.add),m},d3.time={};var fs=Date;ft.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){fu.setUTCDate.apply(this._,arguments)},setDay:function(){fu.setUTCDay.apply(this._,arguments)},setFullYear:function(){fu.setUTCFullYear.apply(this._,arguments)},setHours:function(){fu.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){fu.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){fu.setUTCMinutes.apply(this._,arguments)},setMonth:function(){fu.setUTCMonth.apply(this._,arguments)},setSeconds:function(){fu.setUTCSeconds.apply(this._,arguments)},setTime:function(){fu.setTime.apply(this._,arguments)}};var fu=Date.prototype;d3.time.format=function(a){function c(c){var d=[],e=-1,f=0,g,h;while(++e<b)a.charCodeAt(e)==37&&(d.push(a.substring(f,e),(h=fA[g=a.charAt(++e)])?h(c):g),f=e+1);return d.push(a.substring(f,e)),d.join("")}var b=a.length;return c.parse=function(b){var c={y:1900,m:0,d:1,H:0,M:0,S:0,L:0},d=fv(c,a,b,0);if(d!=b.length)return null;"p"in c&&(c.H=c.H%12+c.p*12);var e=new fs;return e.setFullYear(c.y,c.m,c.d),e.setHours(c.H,c.M,c.S,c.L),e},c.toString=function(){return a},c};var fw=d3.format("02d"),fx=d3.format("03d"),fy=d3.format("04d"),fz=d3.format("2d"),fA={a:function(a){return fG[a.getDay()].substring(0,3)},A:function(a){return fG[a.getDay()]},b:function(a){return fM[a.getMonth()].substring(0,3)},B:function(a){return fM[a.getMonth()]},c:d3.time.format("%a %b %e %H:%M:%S %Y"),d:function(a){return fw(a.getDate())},e:function(a){return fz(a.getDate())},H:function(a){return fw(a.getHours())},I:function(a){return fw(a.getHours()%12||12)},j:function(a){return fx(1+d3.time.dayOfYear(a))},L:function(a){return fx(a.getMilliseconds())},m:function(a){return fw(a.getMonth()+1)},M:function(a){return fw(a.getMinutes())},p:function(a){return a.getHours()>=12?"PM":"AM"},S:function(a){return fw(a.getSeconds())},U:function(a){return fw(d3.time.sundayOfYear(a))},w:function(a){return a.getDay()},W:function(a){return fw(d3.time.mondayOfYear(a))},x:d3.time.format("%m/%d/%y"),X:d3.time.format("%H:%M:%S"),y:function(a){return fw(a.getFullYear()%100)},Y:function(a){return fy(a.getFullYear()%1e4)},Z:ga,"%":function(a){return"%"}},fB={a:fC,A:fD,b:fH,B:fJ,c:fN,d:fU,e:fU,H:fV,I:fV,L:fY,m:fT,M:fW,p:f$,S:fX,x:fO,X:fP,y:fR,Y:fQ},fE=/^(?:sun|mon|tue|wed|thu|fri|sat)/i,fF=/^(?:Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday)/i,fG=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],fI=d3.map({jan:0,feb:1,mar:2,apr:3,may:4,jun:5,jul:6,aug:7,sep:8,oct:9,nov:10,dec:11}),fK=/^(?:January|February|March|April|May|June|July|August|September|October|November|December)/ig,fL=d3.map({january:0,february:1,march:2,april:3,may:4,june:5,july:6,august:7,september:8,october:9,november:10,december:11}),fM=["January","February","March","April","May","June","July","August","September","October","November","December"],fZ=/\s*\d+/,f_=d3.map({am:0,pm:1});d3.time.format.utc=function(a){function c(a){try{fs=ft;var c=new fs;return c._=a,b(c)}finally{fs=Date}}var b=d3.time.format(a);return c.parse=function(a){try{fs=ft;var c=b.parse(a);return c&&c._}finally{fs=Date}},c.toString=b.toString,c};var gb=d3.time.format.utc("%Y-%m-%dT%H:%M:%S.%LZ");d3.time.format.iso=Date.prototype.toISOString?gc:gb,gc.parse=function(a){var b=new Date(a);return isNaN(b)?null:b},gc.toString=gb.toString,d3.time.second=gd(function(a){return new fs(Math.floor(a/1e3)*1e3)},function(a,b){a.setTime(a.getTime()+Math.floor(b)*1e3)},function(a){return a.getSeconds()}),d3.time.seconds=d3.time.second.range,d3.time.seconds.utc=d3.time.second.utc.range,d3.time.minute=gd(function(a){return new fs(Math.floor(a/6e4)*6e4)},function(a,b){a.setTime(a.getTime()+Math.floor(b)*6e4)},function(a){return a.getMinutes()}),d3.time.minutes=d3.time.minute.range,d3.time.minutes.utc=d3.time.minute.utc.range,d3.time.hour=gd(function(a){var b=a.getTimezoneOffset()/60;return new fs((Math.floor(a/36e5-b)+b)*36e5)},function(a,b){a.setTime(a.getTime()+Math.floor(b)*36e5)},function(a){return a.getHours()}),d3.time.hours=d3.time.hour.range,d3.time.hours.utc=d3.time.hour.utc.range,d3.time.day=gd(function(a){var b=new fs(0,a.getMonth(),a.getDate());return b.setFullYear(a.getFullYear()),b},function(a,b){a.setDate(a.getDate()+b)},function(a){return a.getDate()-1}),d3.time.days=d3.time.day.range,d3.time.days.utc=d3.time.day.utc.range,d3.time.dayOfYear=function(a){var b=d3.time.year(a);return Math.floor((a-b-(a.getTimezoneOffset()-b.getTimezoneOffset())*6e4)/864e5)},fG.forEach(function(a,b){a=a.toLowerCase(),b=7-b;var c=d3.time[a]=gd(function(a){return(a=d3.time.day(a)).setDate(a.getDate()-(a.getDay()+b)%7),a},function(a,b){a.setDate(a.getDate()+Math.floor(b)*7)},function(a){var c=d3.time.year(a).getDay();return Math.floor((d3.time.dayOfYear(a)+(c+b)%7)/7)-(c!==b)});d3.time[a+"s"]=c.range,d3.time[a+"s"].utc=c.utc.range,d3.time[a+"OfYear"]=function(a){var c=d3.time.year(a).getDay();return Math.floor((d3.time.dayOfYear(a)+(c+b)%7)/7)}}),d3.time.week=d3.time.sunday,d3.time.weeks=d3.time.sunday.range,d3.time.weeks.utc=d3.time.sunday.utc.range,d3.time.weekOfYear=d3.time.sundayOfYear,d3.time.month=gd(function(a){return a=d3.time.day(a),a.setDate(1),a},function(a,b){a.setMonth(a.getMonth()+b)},function(a){return a.getMonth()}),d3.time.months=d3.time.month.range,d3.time.months.utc=d3.time.month.utc.range,d3.time.year=gd(function(a){return a=d3.time.day(a),a.setMonth(0,1),a},function(a,b){a.setFullYear(a.getFullYear()+b)},function(a){return a.getFullYear()}),d3.time.years=d3.time.year.range,d3.time.years.utc=d3.time.year.utc.range;var gl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],gm=[[d3.time.second,1],[d3.time.second,5],[d3.time.second,15],[d3.time.second,30],[d3.time.minute,1],[d3.time.minute,5],[d3.time.minute,15],[d3.time.minute,30],[d3.time.hour,1],[d3.time.hour,3],[d3.time.hour,6],[d3.time.hour,12],[d3.time.day,1],[d3.time.day,2],[d3.time.week,1],[d3.time.month,1],[d3.time.month,3],[d3.time.year,1]],gn=[[d3.time.format("%Y"),function(a){return!0}],[d3.time.format("%B"),function(a){return a.getMonth()}],[d3.time.format("%b %d"),function(a){return a.getDate()!=1}],[d3.time.format("%a %d"),function(a){return a.getDay()&&a.getDate()!=1}],[d3.time.format("%I %p"),function(a){return a.getHours()}],[d3.time.format("%I:%M"),function(a){return a.getMinutes()}],[d3.time.format(":%S"),function(a){return a.getSeconds()}],[d3.time.format(".%L"),function(a){return a.getMilliseconds()}]],go=d3.scale.linear(),gp=gi(gn);gm.year=function(a,b){return go.domain(a.map(gk)).ticks(b).map(gj)},d3.time.scale=function(){return gf(d3.scale.linear(),gm,gp)};var gq=gm.map(function(a){return[a[0].utc,a[1]]}),gr=[[d3.time.format.utc("%Y"),function(a){return!0}],[d3.time.format.utc("%B"),function(a){return a.getUTCMonth()}],[d3.time.format.utc("%b %d"),function(a){return a.getUTCDate()!=1}],[d3.time.format.utc("%a %d"),function(a){return a.getUTCDay()&&a.getUTCDate()!=1}],[d3.time.format.utc("%I %p"),function(a){return a.getUTCHours()}],[d3.time.format.utc("%I:%M"),function(a){return a.getUTCMinutes()}],[d3.time.format.utc(":%S"),function(a){return a.getUTCSeconds()}],[d3.time.format.utc(".%L"),function(a){return a.getUTCMilliseconds()}]],gs=gi(gr);gq.year=function(a,b){return go.domain(a.map(gu)).ticks(b).map(gt)},d3.time.scale.utc=function(){return gf(d3.scale.linear(),gq,gs)}})(); |
2563 | \ No newline at end of file |
2564 | |
2565 | === added file 'src/schooltool/virginia/browser/resources/virginia.css' |
2566 | --- src/schooltool/virginia/browser/resources/virginia.css 1970-01-01 00:00:00 +0000 |
2567 | +++ src/schooltool/virginia/browser/resources/virginia.css 2012-10-08 23:48:26 +0000 |
2568 | @@ -0,0 +1,110 @@ |
2569 | +/* Filters sidebar */ |
2570 | + |
2571 | +select.report-filter { |
2572 | + width: 140px; |
2573 | +} |
2574 | + |
2575 | +/* Chart report table */ |
2576 | + |
2577 | +.section-report th.skills { |
2578 | + width: 220px; |
2579 | +} |
2580 | + |
2581 | +.section-report .chart-container { |
2582 | + height: 14px; |
2583 | +} |
2584 | + |
2585 | +.section-report tbody td { |
2586 | + vertical-align: middle; |
2587 | +} |
2588 | + |
2589 | +.va-section-report thead th.student { |
2590 | + width: 160px; |
2591 | +} |
2592 | + |
2593 | +/* Chart axis */ |
2594 | + |
2595 | +.axis path, .axis line { |
2596 | + fill: none; |
2597 | + shape-rendering: crispEdges; |
2598 | +} |
2599 | + |
2600 | +.x-axis path, .x-axis line { |
2601 | + stroke: #6C7B7C; |
2602 | +} |
2603 | + |
2604 | +.x-axis text { |
2605 | + font-size: 8px; |
2606 | + fill: #BEAFA8; |
2607 | +} |
2608 | + |
2609 | +.y-axis path, .y-axis line { |
2610 | + stroke: #000; |
2611 | +} |
2612 | + |
2613 | +/* Chart backgrounds */ |
2614 | + |
2615 | +.bg-chart rect { |
2616 | + fill: #D2EBE0; |
2617 | +} |
2618 | + |
2619 | +.bg-passing-target rect { |
2620 | + fill: #fff; |
2621 | +} |
2622 | + |
2623 | +/* "Meets target" gradient */ |
2624 | + |
2625 | +.section-report, .gradient-example { |
2626 | + background: -webkit-linear-gradient(left, #DFF2EA, #F7F7F7); |
2627 | + background: -moz-linear-gradient(left, #DFF2EA, #F7F7F7); |
2628 | +} |
2629 | + |
2630 | +.section-report tr, .gradient-example tr { |
2631 | + background-color: #F7F7F7; |
2632 | +} |
2633 | + |
2634 | +.section-report tr.above-passing-target, |
2635 | +.gradient-example tr.above-passing-target { |
2636 | + background-color: transparent; |
2637 | +} |
2638 | + |
2639 | +.section-report thead tr { |
2640 | + background-color: #6C7B7C; |
2641 | +} |
2642 | + |
2643 | +/* Score Colors help */ |
2644 | + |
2645 | +.score-colors-container form { |
2646 | + margin-top: 0; |
2647 | +} |
2648 | + |
2649 | +.score-colors-container .no-batch-navigation { |
2650 | + margin-top: 0; |
2651 | + padding: 0; |
2652 | +} |
2653 | + |
2654 | +.score-colors-container table { |
2655 | + margin-top: 0; |
2656 | +} |
2657 | + |
2658 | +.score-colors-table { |
2659 | + margin-bottom: 0; |
2660 | +} |
2661 | + |
2662 | +.score-colors-table thead { |
2663 | + display: none; |
2664 | +} |
2665 | + |
2666 | +.score-colors-table tbody { |
2667 | + border-top: none; |
2668 | +} |
2669 | + |
2670 | +.score-color-cell .color { |
2671 | + width: 16px; |
2672 | + display: inline-block; |
2673 | + margin-right: 8px; |
2674 | +} |
2675 | + |
2676 | +.score-color-cell .passing-target { |
2677 | + background-color: #D2EBE0; |
2678 | +} |
2679 | |
2680 | === added file 'src/schooltool/virginia/browser/resources/virginia.js' |
2681 | --- src/schooltool/virginia/browser/resources/virginia.js 1970-01-01 00:00:00 +0000 |
2682 | +++ src/schooltool/virginia/browser/resources/virginia.js 2012-10-08 23:48:26 +0000 |
2683 | @@ -0,0 +1,163 @@ |
2684 | +ST.virginia = function () { |
2685 | + function get_filter_fields() { |
2686 | + return $('.sidebar .report-filter'); |
2687 | + }; |
2688 | + function get_filter_field_values(fields) { |
2689 | + var result = [], |
2690 | + field; |
2691 | + fields.each(function(index, element) { |
2692 | + field = $(element); |
2693 | + if (field.is('select') || field.is('input:checked')) { |
2694 | + result.push({ |
2695 | + name: field.attr('name'), |
2696 | + value: field.val() |
2697 | + }); |
2698 | + } |
2699 | + }); |
2700 | + return result; |
2701 | + }; |
2702 | + function append_filter_fields(container_id) { |
2703 | + var container = $(ST.dialogs.jquery_id(container_id)), |
2704 | + form = container.find('form'), |
2705 | + filter_fields = get_filter_fields(), |
2706 | + filter_field_values = get_filter_field_values(filter_fields), |
2707 | + input_field; |
2708 | + $.each(filter_field_values, function(index, field) { |
2709 | + input_field = $('<input type="hidden" />') |
2710 | + .attr('name', field.name) |
2711 | + .attr('value', field.value); |
2712 | + form.append(input_field); |
2713 | + }); |
2714 | + }; |
2715 | + function get_tick_format(count) { |
2716 | + return function(d, i) { |
2717 | + return Math.round(Math.abs(d*100/count)); |
2718 | + }; |
2719 | + }; |
2720 | + function get_ticks(max, count) { |
2721 | + var delta = max/count; |
2722 | + result = []; |
2723 | + for (i=0; i<=count; i++) { |
2724 | + result.push(i*delta); |
2725 | + result.push(i*-delta); |
2726 | + } |
2727 | + return result; |
2728 | + }; |
2729 | + return { |
2730 | + get_filter_fields: get_filter_fields, |
2731 | + get_filter_field_values: get_filter_field_values, |
2732 | + on_section_report_sort: function(container_id, column_name, |
2733 | + sort_on_name) { |
2734 | + append_filter_fields(container_id); |
2735 | + ST.table.on_form_sort(container_id, column_name, sort_on_name); |
2736 | + }, |
2737 | + build_color_cell: function(container_id, score, colors) { |
2738 | + var passing_colors = d3.interpolateRgb( |
2739 | + colors.passing.start, |
2740 | + colors.passing.end), |
2741 | + not_passing_colors = d3.interpolateRgb( |
2742 | + colors.not_passing.start, |
2743 | + colors.not_passing.end); |
2744 | + container = $(container_id); |
2745 | + color = $('<span class="color"> </span>'); |
2746 | + title = $('<span class="title">'+score.label+'</span>'); |
2747 | + if (score.is_passing) { |
2748 | + colors = passing_colors; |
2749 | + } else { |
2750 | + colors = not_passing_colors; |
2751 | + } |
2752 | + color.css('backgroundColor', colors(score.color_weight)); |
2753 | + container.append(color, title); |
2754 | + }, |
2755 | + build_chart: function(container_id, svg_size, container_size, |
2756 | + container_margins, skills_count, |
2757 | + passing_target_size, scores, colors) { |
2758 | + var g = d3.select(container_id) |
2759 | + .append('svg') |
2760 | + .attr('width', svg_size.width) |
2761 | + .attr('height', svg_size.height) |
2762 | + .attr('class', 'chart') |
2763 | + .append('g') |
2764 | + .attr('transform', |
2765 | + 'translate('+container_margins.left+',0)'), |
2766 | + passing_colors = d3.interpolateRgb( |
2767 | + colors.passing.start, |
2768 | + colors.passing.end), |
2769 | + not_passing_colors = d3.interpolateRgb( |
2770 | + colors.not_passing.start, |
2771 | + colors.not_passing.end), |
2772 | + xTickCount = 5, |
2773 | + xTickSize = 2, |
2774 | + xTickPadding = 2, |
2775 | + xTickValues, |
2776 | + x, |
2777 | + x_axis; |
2778 | + if (skills_count == 0) { |
2779 | + // XXX: no skills evaluated, review this approach |
2780 | + skills_count = 1; |
2781 | + } |
2782 | + xTickValues = get_ticks(skills_count, xTickCount); |
2783 | + x = d3.scale.linear() |
2784 | + .domain([-skills_count, skills_count]) |
2785 | + .range([0, container_size.width]); |
2786 | + x_axis = d3.svg.axis() |
2787 | + .scale(x) |
2788 | + .orient('top') |
2789 | + .tickFormat(get_tick_format(skills_count)) |
2790 | + .tickPadding(xTickPadding) |
2791 | + .tickSize(xTickSize) |
2792 | + .tickValues(xTickValues); |
2793 | + g.append('g') |
2794 | + .attr('class', 'bg-chart') |
2795 | + .append('rect') |
2796 | + .attr('width', container_size.width) |
2797 | + .attr('height', container_size.height); |
2798 | + g.append('g') |
2799 | + .attr('class', 'bg-passing-target') |
2800 | + .append('rect') |
2801 | + .attr('width', passing_target_size.width) |
2802 | + .attr('height', passing_target_size.height); |
2803 | + g.selectAll('rect.score') |
2804 | + .data(scores) |
2805 | + .enter() |
2806 | + .append('rect') |
2807 | + .attr('class', |
2808 | + function(d, i) { |
2809 | + if (d.above_passing_target) { |
2810 | + return 'score above-passing-target'; |
2811 | + } |
2812 | + return 'score'; |
2813 | + }) |
2814 | + .attr('x', function(d,i) {return x(Math.min(0, d.x));}) |
2815 | + .attr('y', 0) |
2816 | + .attr('width', function(d,i) {return Math.abs(x(d.x)-x(0));}) |
2817 | + .attr('height', container_size.height-0.5) |
2818 | + .attr('fill', |
2819 | + function(d, i) { |
2820 | + var colors; |
2821 | + if (d.is_passing) { |
2822 | + colors = passing_colors; |
2823 | + } else { |
2824 | + colors = not_passing_colors; |
2825 | + } |
2826 | + return colors(d.color_weight); |
2827 | + }) |
2828 | + .append('title') |
2829 | + .text(function(d, i) {return d.label + ': ' + d.percentage;}); |
2830 | + $('.above-passing-target').closest('tr') |
2831 | + .addClass('above-passing-target'); |
2832 | + g.append('g') |
2833 | + .attr('class', 'x-axis axis') |
2834 | + .attr('transform', |
2835 | + 'translate(0, '+(container_size.height-0.25)+')') |
2836 | + .call(x_axis); |
2837 | + g.append('g') |
2838 | + .attr('class', 'y-axis axis') |
2839 | + .append('line') |
2840 | + .attr('x1', x(0)) |
2841 | + .attr('x2', x(0)) |
2842 | + .attr('y1', 0) |
2843 | + .attr('y2', svg_size.height); |
2844 | + } |
2845 | + }; |
2846 | +}(); |
2847 | |
2848 | === added directory 'src/schooltool/virginia/browser/stests' |
2849 | === added file 'src/schooltool/virginia/browser/stests/__init__.py' |
2850 | --- src/schooltool/virginia/browser/stests/__init__.py 1970-01-01 00:00:00 +0000 |
2851 | +++ src/schooltool/virginia/browser/stests/__init__.py 2012-10-08 23:48:26 +0000 |
2852 | @@ -0,0 +1,1 @@ |
2853 | +# Python package |
2854 | |
2855 | === added file 'src/schooltool/virginia/browser/stests/section_summary_report.txt' |
2856 | --- src/schooltool/virginia/browser/stests/section_summary_report.txt 1970-01-01 00:00:00 +0000 |
2857 | +++ src/schooltool/virginia/browser/stests/section_summary_report.txt 2012-10-08 23:48:26 +0000 |
2858 | @@ -0,0 +1,867 @@ |
2859 | +Section Summary Report |
2860 | +====================== |
2861 | + |
2862 | +Helpers: |
2863 | + |
2864 | + >>> from pprint import pprint |
2865 | + >>> def gradeStudent(browser, student, scores): |
2866 | + ... for skillset, grades in scores.items(): |
2867 | + ... active = browser.query.css('.third-nav li.active a') |
2868 | + ... if active.text != skillset: |
2869 | + ... link = browser.query.link(skillset) |
2870 | + ... # XXX: fix this, use clicks instead of open |
2871 | + ... browser.open(link.get_attribute('href')) |
2872 | + ... for label, grade in grades: |
2873 | + ... browser.ui.gradebook.worksheet.score(student, label, grade) |
2874 | + ... browser.query.name('UPDATE_SUBMIT').click() |
2875 | + >>> def print_charts_info(browser): |
2876 | + ... for chart in browser.query_all.css('.chart-container'): |
2877 | + ... rects = [] |
2878 | + ... for rect in chart.query_all.css('svg > g > rect'): |
2879 | + ... info = { |
2880 | + ... 'fill': rect.get_attribute('fill'), |
2881 | + ... 'title': rect.query.tag('title').text, |
2882 | + ... 'width': rect.get_attribute('width'), |
2883 | + ... 'x': rect.get_attribute('x'), |
2884 | + ... } |
2885 | + ... rects.append(info) |
2886 | + ... pprint({ |
2887 | + ... 'id': chart.get_attribute('id'), |
2888 | + ... 'rects': rects, |
2889 | + ... }) |
2890 | + >>> def print_score_colors(browser): |
2891 | + ... result = [] |
2892 | + ... sel = '.score-colors-table .score-color-cell' |
2893 | + ... for cell in browser.query_all.css(sel)[:-1]: |
2894 | + ... title = cell.query.css('span.title') |
2895 | + ... color = cell.query.css('span.color') |
2896 | + ... print '%s: %s' % (title.text, color.get_attribute('style')) |
2897 | + |
2898 | +Log in as manager: |
2899 | + |
2900 | + >>> manager = browsers.manager |
2901 | + >>> manager.ui.login('manager', 'schooltool') |
2902 | + |
2903 | +Set up persons: |
2904 | + |
2905 | + >>> manager.ui.person.add('Jeffrey', 'Elkner', 'jeffrey', 'pwd') |
2906 | + >>> manager.ui.person.add('Camila', 'Cerna', 'camila', 'pwd') |
2907 | + >>> manager.ui.person.add('Liliana', 'Vividor', 'liliana', 'pwd') |
2908 | + >>> manager.ui.person.add('Mario', 'Tejada', 'mario', 'pwd') |
2909 | + |
2910 | +Set up a section: |
2911 | + |
2912 | + >>> manager.ui.schoolyear.add('2012', '2012-01-01', '2012-12-31') |
2913 | + >>> manager.ui.term.add('2012', '2012', '2012-01-01', '2012-12-31') |
2914 | + >>> manager.ui.course.add('2012', 'Programming', course_id='PRO') |
2915 | + >>> manager.ui.section.add('2012', '2012', 'Programming') |
2916 | + >>> manager.ui.section.instructors.add('2012', '2012', 'Programming (1)', |
2917 | + ... ['jeffrey']) |
2918 | + >>> manager.ui.section.students.add('2012', '2012', 'Programming (1)', |
2919 | + ... ['camila', 'liliana', 'mario']) |
2920 | + |
2921 | +Set up skills: |
2922 | + |
2923 | + >>> manager.ui.layer.add('Course') |
2924 | + >>> manager.ui.node.add('Programming', label='PRO') |
2925 | + |
2926 | + >>> manager.ui.skillset.add('Python Environment', '01') |
2927 | + >>> manager.ui.skillset.add('Data Types, Statements, and Expressions', '02') |
2928 | + >>> manager.ui.skillset.add('Errors', '03') |
2929 | + >>> manager.ui.skillset.add('Strings', '04') |
2930 | + |
2931 | + >>> manager.ui.skill.add( |
2932 | + ... 'Python Environment', |
2933 | + ... 'Use the Python shell for interactive evaluation', |
2934 | + ... '01') |
2935 | + >>> manager.ui.skill.add( |
2936 | + ... 'Python Environment', |
2937 | + ... 'Make Python scripts with a text editor', |
2938 | + ... '02', |
2939 | + ... required=False) |
2940 | + >>> manager.ui.skill.add( |
2941 | + ... 'Data Types, Statements, and Expressions', |
2942 | + ... 'Recognize int, float, str, list, tuple, and dict.', |
2943 | + ... '03') |
2944 | + >>> manager.ui.skill.add( |
2945 | + ... 'Data Types, Statements, and Expressions', |
2946 | + ... 'Use type(...) to determine the data type of a given value.', |
2947 | + ... '04') |
2948 | + >>> manager.ui.skill.add( |
2949 | + ... 'Data Types, Statements, and Expressions', |
2950 | + ... 'Use assignment statements to assign names (variables) to values.', |
2951 | + ... '05') |
2952 | + >>> manager.ui.skill.add( |
2953 | + ... 'Data Types, Statements, and Expressions', |
2954 | + ... 'Use the input function to read user input.', |
2955 | + ... '06') |
2956 | + >>> manager.ui.skill.add( |
2957 | + ... 'Data Types, Statements, and Expressions', |
2958 | + ... 'Define keyword and recognize keywords in Python.', |
2959 | + ... '07') |
2960 | + >>> manager.ui.skill.add( |
2961 | + ... 'Data Types, Statements, and Expressions', |
2962 | + ... 'Create legal variable names and recognize illegal ones.', |
2963 | + ... '08', |
2964 | + ... required=False) |
2965 | + >>> manager.ui.skill.add( |
2966 | + ... 'Errors', |
2967 | + ... 'Recognize and differentiate among errors.', |
2968 | + ... '09') |
2969 | + >>> manager.ui.skill.add( |
2970 | + ... 'Errors', |
2971 | + ... 'Read and interpret stack traceback messages.', |
2972 | + ... '10') |
2973 | + >>> manager.ui.skill.add( |
2974 | + ... 'Strings', |
2975 | + ... 'Create string literals with single, double, and triple quotes.', |
2976 | + ... '11') |
2977 | + >>> manager.ui.skill.add( |
2978 | + ... 'Strings', |
2979 | + ... 'Use the len() function to return the length of a string.', |
2980 | + ... '12') |
2981 | + >>> manager.ui.skill.add( |
2982 | + ... 'Strings', |
2983 | + ... 'Use the find(...) method to locate substrings in a string.', |
2984 | + ... '13', |
2985 | + ... required=False) |
2986 | + >>> manager.ui.skill.add( |
2987 | + ... 'Strings', |
2988 | + ... 'Use the split() method to break a string into a list.', |
2989 | + ... '14', |
2990 | + ... required=False) |
2991 | + |
2992 | + >>> manager.query.link('Search').click() |
2993 | + >>> manager.query.link('Programming').click() |
2994 | + |
2995 | + >>> sel = '//h2[contains(@class, "ui-accordion-header")]' |
2996 | + >>> accordion_headers = manager.query_all.xpath(sel) |
2997 | + >>> sel = 'div.ui-accordion-content' |
2998 | + >>> accordion_contents = manager.query_all.css(sel) |
2999 | + >>> information_accordion_content = accordion_contents[0] |
3000 | + >>> skillsets_accordion_header = accordion_headers[4] |
3001 | + >>> skillsets_accordion_header.click() |
3002 | + >>> skillsets_accordion_content = accordion_contents[4] |
3003 | + >>> manager.wait_no(information_accordion_content.is_displayed) |
3004 | + >>> sel = '//a[@title="Edit skillsets"]' |
3005 | + >>> manager.query.xpath(sel).click() |
3006 | + >>> manager.query.id('form-buttons-add').click() |
3007 | + >>> manager.query.link('Done').click() |
3008 | + |
3009 | + >>> sel = '//h2[contains(@class, "ui-accordion-header")]' |
3010 | + >>> accordion_headers = manager.query_all.xpath(sel) |
3011 | + >>> sel = 'div.ui-accordion-content' |
3012 | + >>> accordion_contents = manager.query_all.css(sel) |
3013 | + >>> information_accordion_content = accordion_contents[0] |
3014 | + >>> layers_accordion_header = accordion_headers[3] |
3015 | + >>> layers_accordion_header.click() |
3016 | + >>> layers_accordion_content = accordion_contents[3] |
3017 | + >>> manager.wait_no(information_accordion_content.is_displayed) |
3018 | + >>> sel = '//a[@title="Edit layers"]' |
3019 | + >>> manager.query.xpath(sel).click() |
3020 | + >>> manager.query.id('form-buttons-add').click() |
3021 | + |
3022 | +Assign the skills to the course: |
3023 | + |
3024 | + >>> manager.query.link('School').click() |
3025 | + >>> manager.query.link('Courses').click() |
3026 | + >>> manager.query.link('Batch Assign Skills').click() |
3027 | + >>> manager.query.name('course_attr').ui.set_value('Course ID') |
3028 | + >>> manager.query.name('layer').ui.set_value('Course') |
3029 | + >>> manager.query.name('node_attr').ui.set_value('Label') |
3030 | + >>> manager.query.name('SEARCH_BUTTON').click() |
3031 | + >>> manager.query.name('ASSIGN_BUTTON').click() |
3032 | + |
3033 | +Log in as instructor: |
3034 | + |
3035 | + >>> teacher = browsers.teacher |
3036 | + >>> teacher.ui.login('jeffrey', 'pwd') |
3037 | + >>> teacher.query.link('CanDo').click() |
3038 | + |
3039 | +Add scores for the students: |
3040 | + |
3041 | + >>> camila_scores = { |
3042 | + ... '01: Python Environment': [ |
3043 | + ... ('01', '4'), |
3044 | + ... ('02', '3'), |
3045 | + ... ], |
3046 | + ... '02: Data Types, Statements, and Expressions': [ |
3047 | + ... ('03', '2'), |
3048 | + ... ('04', '0'), |
3049 | + ... ('05', '3'), |
3050 | + ... ('06', '3'), |
3051 | + ... ('07', '3'), |
3052 | + ... ('08', '1'), |
3053 | + ... ], |
3054 | + ... '03: Errors': [ |
3055 | + ... ('09', '4'), |
3056 | + ... ('10', '3'), |
3057 | + ... ], |
3058 | + ... '04: Strings': [ |
3059 | + ... ('11', '4'), |
3060 | + ... ('12', '3'), |
3061 | + ... ('13', '3'), |
3062 | + ... ('14', '3'), |
3063 | + ... ], |
3064 | + ... } |
3065 | + >>> gradeStudent(teacher, 'Cerna, Camila', camila_scores) |
3066 | + |
3067 | + >>> mario_scores = { |
3068 | + ... '01: Python Environment': [ |
3069 | + ... ('01', '3'), |
3070 | + ... ('02', '4'), |
3071 | + ... ], |
3072 | + ... '02: Data Types, Statements, and Expressions': [ |
3073 | + ... ('03', '1'), |
3074 | + ... ('04', '0'), |
3075 | + ... ('05', '0'), |
3076 | + ... ('06', '2'), |
3077 | + ... ('07', '2'), |
3078 | + ... ('08', '1'), |
3079 | + ... ], |
3080 | + ... '03: Errors': [ |
3081 | + ... ('09', ''), |
3082 | + ... ('10', '3'), |
3083 | + ... ], |
3084 | + ... '04: Strings': [ |
3085 | + ... ('11', '3'), |
3086 | + ... ('12', '2'), |
3087 | + ... ('13', '1'), |
3088 | + ... ('14', ''), |
3089 | + ... ], |
3090 | + ... } |
3091 | + >>> gradeStudent(teacher, 'Tejada, Mario', mario_scores) |
3092 | + |
3093 | + >>> liliana_scores = { |
3094 | + ... '01: Python Environment': [ |
3095 | + ... ('01', '1'), |
3096 | + ... ('02', ''), |
3097 | + ... ], |
3098 | + ... '02: Data Types, Statements, and Expressions': [ |
3099 | + ... ('03', '2'), |
3100 | + ... ('04', '0'), |
3101 | + ... ('05', '2'), |
3102 | + ... ('06', '4'), |
3103 | + ... ('07', '0'), |
3104 | + ... ('08', '0'), |
3105 | + ... ], |
3106 | + ... '03: Errors': [ |
3107 | + ... ('09', '4'), |
3108 | + ... ('10', '4'), |
3109 | + ... ], |
3110 | + ... '04: Strings': [ |
3111 | + ... ('11', '2'), |
3112 | + ... ('12', '2'), |
3113 | + ... ('13', ''), |
3114 | + ... ('14', ''), |
3115 | + ... ], |
3116 | + ... } |
3117 | + >>> gradeStudent(teacher, 'Vividor, Liliana', liliana_scores) |
3118 | + |
3119 | +Print the Section Summary page details: |
3120 | + |
3121 | + >>> teacher.query.link('Section Summary').click() |
3122 | + >>> print teacher.query.css('.page .header h1').text |
3123 | + Section Summary |
3124 | + >>> print teacher.query.css('.page .header h2').text |
3125 | + Programming (1) |
3126 | + |
3127 | +Print the students individual charts information: |
3128 | + |
3129 | + >>> print_charts_info(teacher) |
3130 | + {'id': u'skills-data-camila', |
3131 | + 'rects': [{'fill': u'#f68e4d', |
3132 | + 'title': u'Uninformed: 7.1%', |
3133 | + 'width': u'21.42857142857143', |
3134 | + 'x': u'78.57142857142857'}, |
3135 | + {'fill': u'#f9b289', |
3136 | + 'title': u'Beginning: 7.1%', |
3137 | + 'width': u'14.285714285714292', |
3138 | + 'x': u'85.71428571428571'}, |
3139 | + {'fill': u'#fcd6c5', |
3140 | + 'title': u'Practicing: 7.1%', |
3141 | + 'width': u'7.142857142857153', |
3142 | + 'x': u'92.85714285714285'}, |
3143 | + {'fill': u'#6b3a89', |
3144 | + 'title': u'Expert: 21.4%', |
3145 | + 'width': u'78.57142857142856', |
3146 | + 'x': u'100'}, |
3147 | + {'fill': u'#d1c4da', |
3148 | + 'title': u'Competent: 57.1%', |
3149 | + 'width': u'57.14285714285714', |
3150 | + 'x': u'100'}]} |
3151 | + {'id': u'skills-data-mario', |
3152 | + 'rects': [{'fill': u'#f68e4d', |
3153 | + 'title': u'Uninformed: 14.3%', |
3154 | + 'width': u'57.142857142857146', |
3155 | + 'x': u'42.857142857142854'}, |
3156 | + {'fill': u'#f9b289', |
3157 | + 'title': u'Beginning: 21.4%', |
3158 | + 'width': u'42.85714285714286', |
3159 | + 'x': u'57.14285714285714'}, |
3160 | + {'fill': u'#fcd6c5', |
3161 | + 'title': u'Practicing: 21.4%', |
3162 | + 'width': u'21.42857142857143', |
3163 | + 'x': u'78.57142857142857'}, |
3164 | + {'fill': u'#6b3a89', |
3165 | + 'title': u'Expert: 7.1%', |
3166 | + 'width': u'28.571428571428555', |
3167 | + 'x': u'100'}, |
3168 | + {'fill': u'#d1c4da', |
3169 | + 'title': u'Competent: 21.4%', |
3170 | + 'width': u'21.428571428571416', |
3171 | + 'x': u'100'}]} |
3172 | + {'id': u'skills-data-liliana', |
3173 | + 'rects': [{'fill': u'#f68e4d', |
3174 | + 'title': u'Uninformed: 21.4%', |
3175 | + 'width': u'57.142857142857146', |
3176 | + 'x': u'42.857142857142854'}, |
3177 | + {'fill': u'#f9b289', |
3178 | + 'title': u'Beginning: 7.1%', |
3179 | + 'width': u'35.71428571428572', |
3180 | + 'x': u'64.28571428571428'}, |
3181 | + {'fill': u'#fcd6c5', |
3182 | + 'title': u'Practicing: 28.6%', |
3183 | + 'width': u'28.571428571428584', |
3184 | + 'x': u'71.42857142857142'}, |
3185 | + {'fill': u'#6b3a89', |
3186 | + 'title': u'Expert: 21.4%', |
3187 | + 'width': u'21.428571428571416', |
3188 | + 'x': u'100'}, |
3189 | + {'fill': u'#d1c4da', |
3190 | + 'title': u'Competent: 0.0%', |
3191 | + 'width': u'0', |
3192 | + 'x': u'100'}]} |
3193 | + |
3194 | +Print the score colors box: |
3195 | + |
3196 | + >>> print_score_colors(teacher) |
3197 | + Expert: background-color: rgb(107, 58, 137); |
3198 | + Competent: background-color: rgb(209, 196, 218); |
3199 | + Practicing: background-color: rgb(252, 214, 197); |
3200 | + Beginning: background-color: rgb(249, 178, 137); |
3201 | + Uninformed: background-color: rgb(246, 142, 77); |
3202 | + |
3203 | +Change the Skill Type filter to Required and print charts: |
3204 | + |
3205 | + >>> table = teacher.query.css('table.section-report') |
3206 | + >>> sel = '//label[text()="Required"]' |
3207 | + >>> teacher.query.xpath(sel).click() |
3208 | + >>> teacher.wait(lambda: table.expired) |
3209 | + >>> print_charts_info(teacher) |
3210 | + {'id': u'skills-data-camila', |
3211 | + 'rects': [{'fill': u'#f68e4d', |
3212 | + 'title': u'Uninformed: 10.0%', |
3213 | + 'width': u'20', |
3214 | + 'x': u'80'}, |
3215 | + {'fill': u'#f9b289', |
3216 | + 'title': u'Beginning: 0.0%', |
3217 | + 'width': u'10', |
3218 | + 'x': u'90'}, |
3219 | + {'fill': u'#fcd6c5', |
3220 | + 'title': u'Practicing: 10.0%', |
3221 | + 'width': u'10', |
3222 | + 'x': u'90'}, |
3223 | + {'fill': u'#6b3a89', |
3224 | + 'title': u'Expert: 30.0%', |
3225 | + 'width': u'80', |
3226 | + 'x': u'100'}, |
3227 | + {'fill': u'#d1c4da', |
3228 | + 'title': u'Competent: 50.0%', |
3229 | + 'width': u'50', |
3230 | + 'x': u'100'}]} |
3231 | + {'id': u'skills-data-mario', |
3232 | + 'rects': [{'fill': u'#f68e4d', |
3233 | + 'title': u'Uninformed: 20.0%', |
3234 | + 'width': u'60', |
3235 | + 'x': u'40'}, |
3236 | + {'fill': u'#f9b289', |
3237 | + 'title': u'Beginning: 10.0%', |
3238 | + 'width': u'39.99999999999999', |
3239 | + 'x': u'60.00000000000001'}, |
3240 | + {'fill': u'#fcd6c5', |
3241 | + 'title': u'Practicing: 30.0%', |
3242 | + 'width': u'30', |
3243 | + 'x': u'70'}, |
3244 | + {'fill': u'#6b3a89', |
3245 | + 'title': u'Expert: 0.0%', |
3246 | + 'width': u'30', |
3247 | + 'x': u'100'}, |
3248 | + {'fill': u'#d1c4da', |
3249 | + 'title': u'Competent: 30.0%', |
3250 | + 'width': u'30', |
3251 | + 'x': u'100'}]} |
3252 | + {'id': u'skills-data-liliana', |
3253 | + 'rects': [{'fill': u'#f68e4d', |
3254 | + 'title': u'Uninformed: 20.0%', |
3255 | + 'width': u'70', |
3256 | + 'x': u'30.000000000000004'}, |
3257 | + {'fill': u'#f9b289', |
3258 | + 'title': u'Beginning: 10.0%', |
3259 | + 'width': u'50', |
3260 | + 'x': u'50'}, |
3261 | + {'fill': u'#fcd6c5', |
3262 | + 'title': u'Practicing: 40.0%', |
3263 | + 'width': u'39.99999999999999', |
3264 | + 'x': u'60.00000000000001'}, |
3265 | + {'fill': u'#6b3a89', |
3266 | + 'title': u'Expert: 30.0%', |
3267 | + 'width': u'30', |
3268 | + 'x': u'100'}, |
3269 | + {'fill': u'#d1c4da', |
3270 | + 'title': u'Competent: 0.0%', |
3271 | + 'width': u'0', |
3272 | + 'x': u'100'}]} |
3273 | + |
3274 | +Change the Skill Type filter to Evaluated and print charts: |
3275 | + |
3276 | + >>> table = teacher.query.css('table.section-report') |
3277 | + >>> sel = '//label[text()="Evaluated"]' |
3278 | + >>> teacher.query.xpath(sel).click() |
3279 | + >>> teacher.wait(lambda: table.expired) |
3280 | + >>> print_charts_info(teacher) |
3281 | + {'id': u'skills-data-camila', |
3282 | + 'rects': [{'fill': u'#f68e4d', |
3283 | + 'title': u'Uninformed: 7.1%', |
3284 | + 'width': u'21.42857142857143', |
3285 | + 'x': u'78.57142857142857'}, |
3286 | + {'fill': u'#f9b289', |
3287 | + 'title': u'Beginning: 7.1%', |
3288 | + 'width': u'14.285714285714292', |
3289 | + 'x': u'85.71428571428571'}, |
3290 | + {'fill': u'#fcd6c5', |
3291 | + 'title': u'Practicing: 7.1%', |
3292 | + 'width': u'7.142857142857153', |
3293 | + 'x': u'92.85714285714285'}, |
3294 | + {'fill': u'#6b3a89', |
3295 | + 'title': u'Expert: 21.4%', |
3296 | + 'width': u'78.57142857142856', |
3297 | + 'x': u'100'}, |
3298 | + {'fill': u'#d1c4da', |
3299 | + 'title': u'Competent: 57.1%', |
3300 | + 'width': u'57.14285714285714', |
3301 | + 'x': u'100'}]} |
3302 | + {'id': u'skills-data-mario', |
3303 | + 'rects': [{'fill': u'#f68e4d', |
3304 | + 'title': u'Uninformed: 16.7%', |
3305 | + 'width': u'66.66666666666667', |
3306 | + 'x': u'33.33333333333333'}, |
3307 | + {'fill': u'#f9b289', |
3308 | + 'title': u'Beginning: 25.0%', |
3309 | + 'width': u'50', |
3310 | + 'x': u'50'}, |
3311 | + {'fill': u'#fcd6c5', |
3312 | + 'title': u'Practicing: 25.0%', |
3313 | + 'width': u'25', |
3314 | + 'x': u'75'}, |
3315 | + {'fill': u'#6b3a89', |
3316 | + 'title': u'Expert: 8.3%', |
3317 | + 'width': u'33.333333333333314', |
3318 | + 'x': u'100'}, |
3319 | + {'fill': u'#d1c4da', |
3320 | + 'title': u'Competent: 25.0%', |
3321 | + 'width': u'25', |
3322 | + 'x': u'100'}]} |
3323 | + {'id': u'skills-data-liliana', |
3324 | + 'rects': [{'fill': u'#f68e4d', |
3325 | + 'title': u'Uninformed: 27.3%', |
3326 | + 'width': u'72.72727272727273', |
3327 | + 'x': u'27.27272727272727'}, |
3328 | + {'fill': u'#f9b289', |
3329 | + 'title': u'Beginning: 9.1%', |
3330 | + 'width': u'45.45454545454546', |
3331 | + 'x': u'54.54545454545454'}, |
3332 | + {'fill': u'#fcd6c5', |
3333 | + 'title': u'Practicing: 36.4%', |
3334 | + 'width': u'36.36363636363637', |
3335 | + 'x': u'63.63636363636363'}, |
3336 | + {'fill': u'#6b3a89', |
3337 | + 'title': u'Expert: 27.3%', |
3338 | + 'width': u'27.272727272727266', |
3339 | + 'x': u'100'}, |
3340 | + {'fill': u'#d1c4da', |
3341 | + 'title': u'Competent: 0.0%', |
3342 | + 'width': u'0', |
3343 | + 'x': u'100'}]} |
3344 | + |
3345 | +Change Passing Score filter to Practicing and print charts: |
3346 | + |
3347 | + >>> table = teacher.query.css('table.section-report') |
3348 | + >>> teacher.query.id('passing-score-filter').ui.set_value('Practicing') |
3349 | + >>> teacher.wait(lambda: table.expired) |
3350 | + >>> print_charts_info(teacher) |
3351 | + {'id': u'skills-data-camila', |
3352 | + 'rects': [{'fill': u'#f68e4d', |
3353 | + 'title': u'Uninformed: 7.1%', |
3354 | + 'width': u'14.285714285714292', |
3355 | + 'x': u'85.71428571428571'}, |
3356 | + {'fill': u'#fcd6c5', |
3357 | + 'title': u'Beginning: 7.1%', |
3358 | + 'width': u'7.142857142857153', |
3359 | + 'x': u'92.85714285714285'}, |
3360 | + {'fill': u'#6b3a89', |
3361 | + 'title': u'Expert: 21.4%', |
3362 | + 'width': u'85.7142857142857', |
3363 | + 'x': u'100'}, |
3364 | + {'fill': u'#9e7fb2', |
3365 | + 'title': u'Competent: 57.1%', |
3366 | + 'width': u'64.28571428571428', |
3367 | + 'x': u'100'}, |
3368 | + {'fill': u'#d1c4da', |
3369 | + 'title': u'Practicing: 7.1%', |
3370 | + 'width': u'7.142857142857139', |
3371 | + 'x': u'100'}]} |
3372 | + {'id': u'skills-data-mario', |
3373 | + 'rects': [{'fill': u'#f68e4d', |
3374 | + 'title': u'Uninformed: 16.7%', |
3375 | + 'width': u'41.66666666666667', |
3376 | + 'x': u'58.33333333333333'}, |
3377 | + {'fill': u'#fcd6c5', |
3378 | + 'title': u'Beginning: 25.0%', |
3379 | + 'width': u'25', |
3380 | + 'x': u'75'}, |
3381 | + {'fill': u'#6b3a89', |
3382 | + 'title': u'Expert: 8.3%', |
3383 | + 'width': u'58.333333333333314', |
3384 | + 'x': u'100'}, |
3385 | + {'fill': u'#9e7fb2', |
3386 | + 'title': u'Competent: 25.0%', |
3387 | + 'width': u'50', |
3388 | + 'x': u'100'}, |
3389 | + {'fill': u'#d1c4da', |
3390 | + 'title': u'Practicing: 25.0%', |
3391 | + 'width': u'25', |
3392 | + 'x': u'100'}]} |
3393 | + {'id': u'skills-data-liliana', |
3394 | + 'rects': [{'fill': u'#f68e4d', |
3395 | + 'title': u'Uninformed: 27.3%', |
3396 | + 'width': u'36.36363636363637', |
3397 | + 'x': u'63.63636363636363'}, |
3398 | + {'fill': u'#fcd6c5', |
3399 | + 'title': u'Beginning: 9.1%', |
3400 | + 'width': u'9.09090909090908', |
3401 | + 'x': u'90.90909090909092'}, |
3402 | + {'fill': u'#6b3a89', |
3403 | + 'title': u'Expert: 27.3%', |
3404 | + 'width': u'63.636363636363654', |
3405 | + 'x': u'100'}, |
3406 | + {'fill': u'#9e7fb2', |
3407 | + 'title': u'Competent: 0.0%', |
3408 | + 'width': u'36.363636363636374', |
3409 | + 'x': u'100'}, |
3410 | + {'fill': u'#d1c4da', |
3411 | + 'title': u'Practicing: 36.4%', |
3412 | + 'width': u'36.363636363636374', |
3413 | + 'x': u'100'}]} |
3414 | + |
3415 | +Print the score colors box: |
3416 | + |
3417 | + >>> print_score_colors(teacher) |
3418 | + Expert: background-color: rgb(107, 58, 137); |
3419 | + Competent: background-color: rgb(158, 127, 178); |
3420 | + Practicing: background-color: rgb(209, 196, 218); |
3421 | + Beginning: background-color: rgb(252, 214, 197); |
3422 | + Uninformed: background-color: rgb(246, 142, 77); |
3423 | + |
3424 | +Print students above passing target: |
3425 | + |
3426 | + >>> sel = '#section-report-form-container tr.above-passing-target' |
3427 | + >>> for row in teacher.query_all.css(sel): |
3428 | + ... print row |
3429 | + <tr class="above-passing-target"> |
3430 | + <td> |
3431 | + Cerna, Camila |
3432 | + </td> |
3433 | + ... |
3434 | + </tr> |
3435 | + |
3436 | +Change % Passing Target filter to 60% and print students above passing |
3437 | +target: |
3438 | + |
3439 | + >>> table = teacher.query.css('table.section-report') |
3440 | + >>> teacher.query.id('passing-target-filter').ui.set_value('60%') |
3441 | + >>> teacher.wait(lambda: table.expired) |
3442 | + >>> sel = '#section-report-form-container tr.above-passing-target' |
3443 | + >>> for row in teacher.query_all.css(sel): |
3444 | + ... print row |
3445 | + <tr class="above-passing-target"> |
3446 | + <td> |
3447 | + Cerna, Camila |
3448 | + </td> |
3449 | + ... |
3450 | + </tr> |
3451 | + <tr class="above-passing-target"> |
3452 | + <td> |
3453 | + Vividor, Liliana |
3454 | + </td> |
3455 | + ... |
3456 | + </tr> |
3457 | + |
3458 | +Print the axis of Camila's chart: |
3459 | + |
3460 | + >>> sel = '#skills-data-camila svg g.axis' |
3461 | + >>> for axis in teacher.query_all.css(sel): |
3462 | + ... print axis |
3463 | + <g class="x-axis axis" transform="translate(0, 11.75)"> |
3464 | + <g style="opacity: 1; " transform="translate(100,0)"> |
3465 | + <line class="tick" x2="0" y2="-2"> |
3466 | + </line> |
3467 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3468 | + 0 |
3469 | + </text> |
3470 | + </g> |
3471 | + <g style="opacity: 1; " transform="translate(100,0)"> |
3472 | + <line class="tick" x2="0" y2="-2"> |
3473 | + </line> |
3474 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3475 | + 0 |
3476 | + </text> |
3477 | + </g> |
3478 | + <g style="opacity: 1; " transform="translate(120,0)"> |
3479 | + <line class="tick" x2="0" y2="-2"> |
3480 | + </line> |
3481 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3482 | + 20 |
3483 | + </text> |
3484 | + </g> |
3485 | + <g style="opacity: 1; " transform="translate(80,0)"> |
3486 | + <line class="tick" x2="0" y2="-2"> |
3487 | + </line> |
3488 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3489 | + 20 |
3490 | + </text> |
3491 | + </g> |
3492 | + <g style="opacity: 1; " transform="translate(140,0)"> |
3493 | + <line class="tick" x2="0" y2="-2"> |
3494 | + </line> |
3495 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3496 | + 40 |
3497 | + </text> |
3498 | + </g> |
3499 | + <g style="opacity: 1; " transform="translate(60,0)"> |
3500 | + <line class="tick" x2="0" y2="-2"> |
3501 | + </line> |
3502 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3503 | + 40 |
3504 | + </text> |
3505 | + </g> |
3506 | + <g style="opacity: 1; " transform="translate(160,0)"> |
3507 | + <line class="tick" x2="0" y2="-2"> |
3508 | + </line> |
3509 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3510 | + 60 |
3511 | + </text> |
3512 | + </g> |
3513 | + <g style="opacity: 1; " transform="translate(40.00000000000001,0)"> |
3514 | + <line class="tick" x2="0" y2="-2"> |
3515 | + </line> |
3516 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3517 | + 60 |
3518 | + </text> |
3519 | + </g> |
3520 | + <g style="opacity: 1; " transform="translate(179.99999999999997,0)"> |
3521 | + <line class="tick" x2="0" y2="-2"> |
3522 | + </line> |
3523 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3524 | + 80 |
3525 | + </text> |
3526 | + </g> |
3527 | + <g style="opacity: 1; " transform="translate(20.000000000000004,0)"> |
3528 | + <line class="tick" x2="0" y2="-2"> |
3529 | + </line> |
3530 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3531 | + 80 |
3532 | + </text> |
3533 | + </g> |
3534 | + <g style="opacity: 1; " transform="translate(200,0)"> |
3535 | + <line class="tick" x2="0" y2="-2"> |
3536 | + </line> |
3537 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3538 | + 100 |
3539 | + </text> |
3540 | + </g> |
3541 | + <g style="opacity: 1; " transform="translate(0,0)"> |
3542 | + <line class="tick" x2="0" y2="-2"> |
3543 | + </line> |
3544 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3545 | + 100 |
3546 | + </text> |
3547 | + </g> |
3548 | + <path class="domain" d="M0,-2V0H200V-2"> |
3549 | + </path> |
3550 | + </g> |
3551 | + <g class="y-axis axis"> |
3552 | + <line x1="100" x2="100" y1="0" y2="12"> |
3553 | + </line> |
3554 | + </g> |
3555 | + |
3556 | + |
3557 | +Evaluated skill type option with no scores |
3558 | +------------------------------------------ |
3559 | + |
3560 | + >>> manager.ui.person.add('Tom', 'Hoffman', 'tom', 'pwd') |
3561 | + >>> manager.ui.person.add('German', 'Tejada', 'german', 'pwd') |
3562 | + >>> manager.ui.person.add('Karla', 'Vividor', 'karla', 'pwd') |
3563 | + >>> manager.ui.section.add('2012', '2012', 'Programming') |
3564 | + >>> manager.ui.section.instructors.add('2012', '2012', 'Programming (2)', |
3565 | + ... ['tom']) |
3566 | + >>> manager.ui.section.students.add('2012', '2012', 'Programming (2)', |
3567 | + ... ['german', 'karla']) |
3568 | + |
3569 | +Log in as instructor: |
3570 | + |
3571 | + >>> another_teacher = browsers.another_teacher |
3572 | + >>> another_teacher.ui.login('tom', 'pwd') |
3573 | + >>> another_teacher.query.link('CanDo').click() |
3574 | + |
3575 | + >>> another_teacher.query.link('Section Summary').click() |
3576 | + >>> print another_teacher.query.css('.page .header h1').text |
3577 | + Section Summary |
3578 | + >>> print another_teacher.query.css('.page .header h2').text |
3579 | + Programming (2) |
3580 | + |
3581 | + >>> table = another_teacher.query.css('table.section-report') |
3582 | + >>> sel = '//label[text()="Evaluated"]' |
3583 | + >>> another_teacher.query.xpath(sel).click() |
3584 | + >>> another_teacher.wait(lambda: table.expired) |
3585 | + >>> print_charts_info(another_teacher) |
3586 | + {'id': u'skills-data-german', |
3587 | + 'rects': [{'fill': u'#f68e4d', |
3588 | + 'title': u'Uninformed: 0.0%', |
3589 | + 'width': u'0', |
3590 | + 'x': u'100'}, |
3591 | + {'fill': u'#f9b289', |
3592 | + 'title': u'Beginning: 0.0%', |
3593 | + 'width': u'0', |
3594 | + 'x': u'100'}, |
3595 | + {'fill': u'#fcd6c5', |
3596 | + 'title': u'Practicing: 0.0%', |
3597 | + 'width': u'0', |
3598 | + 'x': u'100'}, |
3599 | + {'fill': u'#6b3a89', |
3600 | + 'title': u'Expert: 0.0%', |
3601 | + 'width': u'0', |
3602 | + 'x': u'100'}, |
3603 | + {'fill': u'#d1c4da', |
3604 | + 'title': u'Competent: 0.0%', |
3605 | + 'width': u'0', |
3606 | + 'x': u'100'}]} |
3607 | + {'id': u'skills-data-karla', |
3608 | + 'rects': [{'fill': u'#f68e4d', |
3609 | + 'title': u'Uninformed: 0.0%', |
3610 | + 'width': u'0', |
3611 | + 'x': u'100'}, |
3612 | + {'fill': u'#f9b289', |
3613 | + 'title': u'Beginning: 0.0%', |
3614 | + 'width': u'0', |
3615 | + 'x': u'100'}, |
3616 | + {'fill': u'#fcd6c5', |
3617 | + 'title': u'Practicing: 0.0%', |
3618 | + 'width': u'0', |
3619 | + 'x': u'100'}, |
3620 | + {'fill': u'#6b3a89', |
3621 | + 'title': u'Expert: 0.0%', |
3622 | + 'width': u'0', |
3623 | + 'x': u'100'}, |
3624 | + {'fill': u'#d1c4da', |
3625 | + 'title': u'Competent: 0.0%', |
3626 | + 'width': u'0', |
3627 | + 'x': u'100'}]} |
3628 | + |
3629 | +Print the axis of Karla's chart: |
3630 | + |
3631 | + >>> sel = '#skills-data-karla svg g.axis' |
3632 | + >>> for axis in another_teacher.query_all.css(sel): |
3633 | + ... print axis |
3634 | + <g class="x-axis axis" transform="translate(0, 11.75)"> |
3635 | + <g style="opacity: 1; " transform="translate(100,0)"> |
3636 | + <line class="tick" x2="0" y2="-2"> |
3637 | + </line> |
3638 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3639 | + 0 |
3640 | + </text> |
3641 | + </g> |
3642 | + <g style="opacity: 1; " transform="translate(100,0)"> |
3643 | + <line class="tick" x2="0" y2="-2"> |
3644 | + </line> |
3645 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3646 | + 0 |
3647 | + </text> |
3648 | + </g> |
3649 | + <g style="opacity: 1; " transform="translate(120,0)"> |
3650 | + <line class="tick" x2="0" y2="-2"> |
3651 | + </line> |
3652 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3653 | + 20 |
3654 | + </text> |
3655 | + </g> |
3656 | + <g style="opacity: 1; " transform="translate(80,0)"> |
3657 | + <line class="tick" x2="0" y2="-2"> |
3658 | + </line> |
3659 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3660 | + 20 |
3661 | + </text> |
3662 | + </g> |
3663 | + <g style="opacity: 1; " transform="translate(140,0)"> |
3664 | + <line class="tick" x2="0" y2="-2"> |
3665 | + </line> |
3666 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3667 | + 40 |
3668 | + </text> |
3669 | + </g> |
3670 | + <g style="opacity: 1; " transform="translate(60,0)"> |
3671 | + <line class="tick" x2="0" y2="-2"> |
3672 | + </line> |
3673 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3674 | + 40 |
3675 | + </text> |
3676 | + </g> |
3677 | + <g style="opacity: 1; " transform="translate(160,0)"> |
3678 | + <line class="tick" x2="0" y2="-2"> |
3679 | + </line> |
3680 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3681 | + 60 |
3682 | + </text> |
3683 | + </g> |
3684 | + <g style="opacity: 1; " transform="translate(39.99999999999999,0)"> |
3685 | + <line class="tick" x2="0" y2="-2"> |
3686 | + </line> |
3687 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3688 | + 60 |
3689 | + </text> |
3690 | + </g> |
3691 | + <g style="opacity: 1; " transform="translate(180,0)"> |
3692 | + <line class="tick" x2="0" y2="-2"> |
3693 | + </line> |
3694 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3695 | + 80 |
3696 | + </text> |
3697 | + </g> |
3698 | + <g style="opacity: 1; " transform="translate(19.999999999999996,0)"> |
3699 | + <line class="tick" x2="0" y2="-2"> |
3700 | + </line> |
3701 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3702 | + 80 |
3703 | + </text> |
3704 | + </g> |
3705 | + <g style="opacity: 1; " transform="translate(200,0)"> |
3706 | + <line class="tick" x2="0" y2="-2"> |
3707 | + </line> |
3708 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3709 | + 100 |
3710 | + </text> |
3711 | + </g> |
3712 | + <g style="opacity: 1; " transform="translate(0,0)"> |
3713 | + <line class="tick" x2="0" y2="-2"> |
3714 | + </line> |
3715 | + <text dy="0em" text-anchor="middle" x="0" y="-4"> |
3716 | + 100 |
3717 | + </text> |
3718 | + </g> |
3719 | + <path class="domain" d="M0,-2V0H200V-2"> |
3720 | + </path> |
3721 | + </g> |
3722 | + <g class="y-axis axis"> |
3723 | + <line x1="100" x2="100" y1="0" y2="12"> |
3724 | + </line> |
3725 | + </g> |
3726 | |
3727 | === added file 'src/schooltool/virginia/browser/stests/test_selenium.py' |
3728 | --- src/schooltool/virginia/browser/stests/test_selenium.py 1970-01-01 00:00:00 +0000 |
3729 | +++ src/schooltool/virginia/browser/stests/test_selenium.py 2012-10-08 23:48:26 +0000 |
3730 | @@ -0,0 +1,33 @@ |
3731 | +# |
3732 | +# SchoolTool - common information systems platform for school administration |
3733 | +# Copyright (c) 2012 Shuttleworth Foundation |
3734 | +# |
3735 | +# This program is free software; you can redistribute it and/or modify |
3736 | +# it under the terms of the GNU General Public License as published by |
3737 | +# the Free Software Foundation; either version 2 of the License, or |
3738 | +# (at your option) any later version. |
3739 | +# |
3740 | +# This program is distributed in the hope that it will be useful, |
3741 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3742 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3743 | +# GNU General Public License for more details. |
3744 | +# |
3745 | +# You should have received a copy of the GNU General Public License |
3746 | +# along with this program; if not, write to the Free Software |
3747 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
3748 | +# |
3749 | +""" |
3750 | +Functional selenium tests for schooltool.virginia |
3751 | +""" |
3752 | +import unittest |
3753 | + |
3754 | +from schooltool.testing.selenium import collect_ftests |
3755 | +from schooltool.virginia.stesting import virginia_selenium_layer |
3756 | + |
3757 | + |
3758 | +def test_suite(): |
3759 | + return collect_ftests(layer=virginia_selenium_layer) |
3760 | + |
3761 | + |
3762 | +if __name__ == '__main__': |
3763 | + unittest.main(defaultTest='test_suite') |
3764 | |
3765 | === added file 'src/schooltool/virginia/browser/stests/va_section_report.txt' |
3766 | --- src/schooltool/virginia/browser/stests/va_section_report.txt 1970-01-01 00:00:00 +0000 |
3767 | +++ src/schooltool/virginia/browser/stests/va_section_report.txt 2012-10-08 23:48:26 +0000 |
3768 | @@ -0,0 +1,358 @@ |
3769 | +Virginia Section Report |
3770 | +======================= |
3771 | + |
3772 | +Helpers: |
3773 | + |
3774 | + >>> from schooltool.testing.util import format_table |
3775 | + >>> from pprint import pprint |
3776 | + >>> def gradeStudent(browser, student, scores): |
3777 | + ... for skillset, grades in scores.items(): |
3778 | + ... active = browser.query.css('.third-nav li.active a') |
3779 | + ... if active.text != skillset: |
3780 | + ... link = browser.query.link(skillset) |
3781 | + ... # XXX: fix this, use clicks instead of open |
3782 | + ... browser.open(link.get_attribute('href')) |
3783 | + ... for label, grade in grades: |
3784 | + ... browser.ui.gradebook.worksheet.score(student, label, grade) |
3785 | + ... browser.query.name('UPDATE_SUBMIT').click() |
3786 | + >>> def print_charts_info(browser): |
3787 | + ... for chart in browser.query_all.css('.chart-container'): |
3788 | + ... rects = [] |
3789 | + ... for rect in chart.query_all.css('svg > g > rect'): |
3790 | + ... info = { |
3791 | + ... 'fill': rect.get_attribute('fill'), |
3792 | + ... 'title': rect.query.tag('title').text, |
3793 | + ... 'width': rect.get_attribute('width'), |
3794 | + ... 'x': rect.get_attribute('x'), |
3795 | + ... } |
3796 | + ... rects.append(info) |
3797 | + ... pprint({ |
3798 | + ... 'id': chart.get_attribute('id'), |
3799 | + ... 'rects': rects, |
3800 | + ... }) |
3801 | + >>> def print_score_colors(browser): |
3802 | + ... result = [] |
3803 | + ... sel = '.score-colors-table .score-color-cell' |
3804 | + ... for cell in browser.query_all.css(sel)[:-1]: |
3805 | + ... title = cell.query.css('span.title') |
3806 | + ... color = cell.query.css('span.color') |
3807 | + ... print '%s: %s' % (title.text, color.get_attribute('style')) |
3808 | + >>> def print_va_section_report_table(browser): |
3809 | + ... rows = [] |
3810 | + ... sel = 'table.va-section-report tbody tr' |
3811 | + ... for tr in browser.query_all.css(sel): |
3812 | + ... row = [] |
3813 | + ... for col in tr.query_all.tag('td'): |
3814 | + ... row.append(col.text) |
3815 | + ... rows.append(row) |
3816 | + ... print format_table(rows) |
3817 | + ... rows = [] |
3818 | + ... sel = 'table.attainment tbody tr' |
3819 | + ... for tr in browser.query_all.css(sel): |
3820 | + ... row = [] |
3821 | + ... for col in tr.query_all.tag('td'): |
3822 | + ... row.append(col.text) |
3823 | + ... rows.append(row) |
3824 | + ... print format_table(rows) |
3825 | + ... rows = [] |
3826 | + ... sel = 'table.attainment-above80 tbody tr' |
3827 | + ... for tr in browser.query_all.css(sel): |
3828 | + ... row = [] |
3829 | + ... for col in tr.query_all.tag('td'): |
3830 | + ... row.append(col.text) |
3831 | + ... rows.append(row) |
3832 | + ... print format_table(rows) |
3833 | + |
3834 | +Log in as manager: |
3835 | + |
3836 | + >>> manager = browsers.manager |
3837 | + >>> manager.ui.login('manager', 'schooltool') |
3838 | + |
3839 | +Set up persons: |
3840 | + |
3841 | + >>> manager.ui.person.add('Jeffrey', 'Elkner', 'jeffrey', 'pwd') |
3842 | + >>> manager.ui.person.add('Camila', 'Cerna', 'camila', 'pwd', ID='cc') |
3843 | + >>> manager.ui.person.add('Liliana', 'Vividor', 'liliana', 'pwd') |
3844 | + >>> manager.ui.person.add('Mario', 'Tejada', 'mario', 'pwd', ID='mt') |
3845 | + |
3846 | +Set up a section: |
3847 | + |
3848 | + >>> manager.ui.schoolyear.add('2012', '2012-01-01', '2012-12-31') |
3849 | + >>> manager.ui.term.add('2012', '2012', '2012-01-01', '2012-12-31') |
3850 | + >>> manager.ui.course.add('2012', 'Programming', course_id='PRO') |
3851 | + >>> manager.ui.section.add('2012', '2012', 'Programming') |
3852 | + >>> manager.ui.section.instructors.add('2012', '2012', 'Programming (1)', |
3853 | + ... ['jeffrey']) |
3854 | + >>> manager.ui.section.students.add('2012', '2012', 'Programming (1)', |
3855 | + ... ['camila', 'liliana', 'mario']) |
3856 | + |
3857 | +Set up skills: |
3858 | + |
3859 | + >>> manager.ui.layer.add('Course') |
3860 | + >>> manager.ui.node.add('Programming', label='PRO') |
3861 | + |
3862 | + >>> manager.ui.skillset.add('Python Environment', '01') |
3863 | + >>> manager.ui.skillset.add('Data Types, Statements, and Expressions', '02') |
3864 | + >>> manager.ui.skillset.add('Errors', '03') |
3865 | + >>> manager.ui.skillset.add('Strings', '04') |
3866 | + |
3867 | + >>> manager.ui.skill.add( |
3868 | + ... 'Python Environment', |
3869 | + ... 'Use the Python shell for interactive evaluation', |
3870 | + ... '01') |
3871 | + >>> manager.ui.skill.add( |
3872 | + ... 'Python Environment', |
3873 | + ... 'Make Python scripts with a text editor', |
3874 | + ... '02', |
3875 | + ... required=False) |
3876 | + >>> manager.ui.skill.add( |
3877 | + ... 'Data Types, Statements, and Expressions', |
3878 | + ... 'Recognize int, float, str, list, tuple, and dict.', |
3879 | + ... '03') |
3880 | + >>> manager.ui.skill.add( |
3881 | + ... 'Data Types, Statements, and Expressions', |
3882 | + ... 'Use type(...) to determine the data type of a given value.', |
3883 | + ... '04') |
3884 | + >>> manager.ui.skill.add( |
3885 | + ... 'Data Types, Statements, and Expressions', |
3886 | + ... 'Use assignment statements to assign names (variables) to values.', |
3887 | + ... '05') |
3888 | + >>> manager.ui.skill.add( |
3889 | + ... 'Data Types, Statements, and Expressions', |
3890 | + ... 'Use the input function to read user input.', |
3891 | + ... '06') |
3892 | + >>> manager.ui.skill.add( |
3893 | + ... 'Data Types, Statements, and Expressions', |
3894 | + ... 'Define keyword and recognize keywords in Python.', |
3895 | + ... '07') |
3896 | + >>> manager.ui.skill.add( |
3897 | + ... 'Data Types, Statements, and Expressions', |
3898 | + ... 'Create legal variable names and recognize illegal ones.', |
3899 | + ... '08', |
3900 | + ... required=False) |
3901 | + >>> manager.ui.skill.add( |
3902 | + ... 'Errors', |
3903 | + ... 'Recognize and differentiate among errors.', |
3904 | + ... '09') |
3905 | + >>> manager.ui.skill.add( |
3906 | + ... 'Errors', |
3907 | + ... 'Read and interpret stack traceback messages.', |
3908 | + ... '10') |
3909 | + >>> manager.ui.skill.add( |
3910 | + ... 'Strings', |
3911 | + ... 'Create string literals with single, double, and triple quotes.', |
3912 | + ... '11') |
3913 | + >>> manager.ui.skill.add( |
3914 | + ... 'Strings', |
3915 | + ... 'Use the len() function to return the length of a string.', |
3916 | + ... '12') |
3917 | + >>> manager.ui.skill.add( |
3918 | + ... 'Strings', |
3919 | + ... 'Use the find(...) method to locate substrings in a string.', |
3920 | + ... '13', |
3921 | + ... required=False) |
3922 | + >>> manager.ui.skill.add( |
3923 | + ... 'Strings', |
3924 | + ... 'Use the split() method to break a string into a list.', |
3925 | + ... '14', |
3926 | + ... required=False) |
3927 | + |
3928 | + >>> manager.query.link('Search').click() |
3929 | + >>> manager.query.link('Programming').click() |
3930 | + |
3931 | + >>> sel = '//h2[contains(@class, "ui-accordion-header")]' |
3932 | + >>> accordion_headers = manager.query_all.xpath(sel) |
3933 | + >>> sel = 'div.ui-accordion-content' |
3934 | + >>> accordion_contents = manager.query_all.css(sel) |
3935 | + >>> information_accordion_content = accordion_contents[0] |
3936 | + >>> skillsets_accordion_header = accordion_headers[4] |
3937 | + >>> skillsets_accordion_header.click() |
3938 | + >>> skillsets_accordion_content = accordion_contents[4] |
3939 | + >>> manager.wait_no(information_accordion_content.is_displayed) |
3940 | + >>> sel = '//a[@title="Edit skillsets"]' |
3941 | + >>> manager.query.xpath(sel).click() |
3942 | + >>> manager.query.id('form-buttons-add').click() |
3943 | + >>> manager.query.link('Done').click() |
3944 | + |
3945 | + >>> sel = '//h2[contains(@class, "ui-accordion-header")]' |
3946 | + >>> accordion_headers = manager.query_all.xpath(sel) |
3947 | + >>> sel = 'div.ui-accordion-content' |
3948 | + >>> accordion_contents = manager.query_all.css(sel) |
3949 | + >>> information_accordion_content = accordion_contents[0] |
3950 | + >>> layers_accordion_header = accordion_headers[3] |
3951 | + >>> layers_accordion_header.click() |
3952 | + >>> layers_accordion_content = accordion_contents[3] |
3953 | + >>> manager.wait_no(information_accordion_content.is_displayed) |
3954 | + >>> sel = '//a[@title="Edit layers"]' |
3955 | + >>> manager.query.xpath(sel).click() |
3956 | + >>> manager.query.id('form-buttons-add').click() |
3957 | + |
3958 | +Assign the skills to the course: |
3959 | + |
3960 | + >>> manager.query.link('School').click() |
3961 | + >>> manager.query.link('Courses').click() |
3962 | + >>> manager.query.link('Batch Assign Skills').click() |
3963 | + >>> manager.query.name('course_attr').ui.set_value('Course ID') |
3964 | + >>> manager.query.name('layer').ui.set_value('Course') |
3965 | + >>> manager.query.name('node_attr').ui.set_value('Label') |
3966 | + >>> manager.query.name('SEARCH_BUTTON').click() |
3967 | + >>> manager.query.name('ASSIGN_BUTTON').click() |
3968 | + |
3969 | +Log in as instructor: |
3970 | + |
3971 | + >>> teacher = browsers.teacher |
3972 | + >>> teacher.ui.login('jeffrey', 'pwd') |
3973 | + >>> teacher.query.link('CanDo').click() |
3974 | + |
3975 | +Print the Virginia Section Report page details: |
3976 | + |
3977 | + >>> teacher.query.link('VA Section Report').click() |
3978 | + >>> print teacher.query.css('.page .header h1').text |
3979 | + Virginia Section Report |
3980 | + >>> print teacher.query.css('.page .header h2').text |
3981 | + Programming (1) |
3982 | + |
3983 | +And the tables with no data: |
3984 | + |
3985 | + >>> print_va_section_report_table(teacher) |
3986 | + +------------------+----+----+---+---+----+----+----+---+---+----+ |
3987 | + | Cerna, Camila | cc | 10 | 0 | 0 | 0% | 0% | 14 | 0 | 0 | 0% | |
3988 | + | Tejada, Mario | mt | 10 | 0 | 0 | 0% | 0% | 14 | 0 | 0 | 0% | |
3989 | + | Vividor, Liliana | | 10 | 0 | 0 | 0% | 0% | 14 | 0 | 0 | 0% | |
3990 | + +------------------+----+----+---+---+----+----+----+---+---+----+ |
3991 | + +---------+---+------+ |
3992 | + | 90-100% | 0 | 0% | |
3993 | + | 80-89% | 0 | 0% | |
3994 | + | 70-79% | 0 | 0% | |
3995 | + | 60-69% | 0 | 0% | |
3996 | + | <= 59% | 3 | 100% | |
3997 | + +---------+---+------+ |
3998 | + +--------------+---+------+ |
3999 | + | 80% or above | 0 | 0% | |
4000 | + | Below 80% | 3 | 100% | |
4001 | + +--------------+---+------+ |
4002 | + |
4003 | + >>> teacher.query.link('Done').click() |
4004 | + |
4005 | +Add scores for the students: |
4006 | + |
4007 | + >>> camila_scores = { |
4008 | + ... '01: Python Environment': [ |
4009 | + ... ('01', '4'), |
4010 | + ... ('02', '3'), |
4011 | + ... ], |
4012 | + ... '02: Data Types, Statements, and Expressions': [ |
4013 | + ... ('03', '2'), |
4014 | + ... ('04', '0'), |
4015 | + ... ('05', '3'), |
4016 | + ... ('06', '3'), |
4017 | + ... ('07', '3'), |
4018 | + ... ('08', '1'), |
4019 | + ... ], |
4020 | + ... '03: Errors': [ |
4021 | + ... ('09', '4'), |
4022 | + ... ('10', '3'), |
4023 | + ... ], |
4024 | + ... '04: Strings': [ |
4025 | + ... ('11', '4'), |
4026 | + ... ('12', '3'), |
4027 | + ... ('13', '3'), |
4028 | + ... ('14', '3'), |
4029 | + ... ], |
4030 | + ... } |
4031 | + >>> gradeStudent(teacher, 'Cerna, Camila', camila_scores) |
4032 | + |
4033 | + >>> mario_scores = { |
4034 | + ... '01: Python Environment': [ |
4035 | + ... ('01', '3'), |
4036 | + ... ('02', '4'), |
4037 | + ... ], |
4038 | + ... '02: Data Types, Statements, and Expressions': [ |
4039 | + ... ('03', '1'), |
4040 | + ... ('04', '0'), |
4041 | + ... ('05', '0'), |
4042 | + ... ('06', '2'), |
4043 | + ... ('07', '2'), |
4044 | + ... ('08', '1'), |
4045 | + ... ], |
4046 | + ... '03: Errors': [ |
4047 | + ... ('09', ''), |
4048 | + ... ('10', '3'), |
4049 | + ... ], |
4050 | + ... '04: Strings': [ |
4051 | + ... ('11', '3'), |
4052 | + ... ('12', '2'), |
4053 | + ... ('13', '1'), |
4054 | + ... ('14', ''), |
4055 | + ... ], |
4056 | + ... } |
4057 | + >>> gradeStudent(teacher, 'Tejada, Mario', mario_scores) |
4058 | + |
4059 | + >>> liliana_scores = { |
4060 | + ... '01: Python Environment': [ |
4061 | + ... ('01', '1'), |
4062 | + ... ('02', ''), |
4063 | + ... ], |
4064 | + ... '02: Data Types, Statements, and Expressions': [ |
4065 | + ... ('03', '2'), |
4066 | + ... ('04', '0'), |
4067 | + ... ('05', '2'), |
4068 | + ... ('06', '4'), |
4069 | + ... ('07', '0'), |
4070 | + ... ('08', '0'), |
4071 | + ... ], |
4072 | + ... '03: Errors': [ |
4073 | + ... ('09', '4'), |
4074 | + ... ('10', '4'), |
4075 | + ... ], |
4076 | + ... '04: Strings': [ |
4077 | + ... ('11', '2'), |
4078 | + ... ('12', '2'), |
4079 | + ... ('13', ''), |
4080 | + ... ('14', ''), |
4081 | + ... ], |
4082 | + ... } |
4083 | + >>> gradeStudent(teacher, 'Vividor, Liliana', liliana_scores) |
4084 | + |
4085 | +Print the report tables again: |
4086 | + |
4087 | + >>> teacher.query.link('VA Section Report').click() |
4088 | + >>> print_va_section_report_table(teacher) |
4089 | + +------------------+----+----+----+---+-----+-----+----+----+----+-----+ |
4090 | + | Cerna, Camila | cc | 10 | 10 | 8 | 80% | 80% | 14 | 14 | 11 | 79% | |
4091 | + | Tejada, Mario | mt | 10 | 9 | 3 | 33% | 30% | 14 | 12 | 4 | 33% | |
4092 | + | Vividor, Liliana | | 10 | 10 | 3 | 30% | 30% | 14 | 11 | 3 | 27% | |
4093 | + +------------------+----+----+----+---+-----+-----+----+----+----+-----+ |
4094 | + +---------+---+-----+ |
4095 | + | 90-100% | 0 | 0% | |
4096 | + | 80-89% | 1 | 33% | |
4097 | + | 70-79% | 0 | 0% | |
4098 | + | 60-69% | 0 | 0% | |
4099 | + | <= 59% | 2 | 67% | |
4100 | + +---------+---+-----+ |
4101 | + +--------------+---+-----+ |
4102 | + | 80% or above | 1 | 33% | |
4103 | + | Below 80% | 2 | 67% | |
4104 | + +--------------+---+-----+ |
4105 | + |
4106 | +Change the Minimum rating score: |
4107 | + |
4108 | + >>> teacher.query.id('passing-score-filter').ui.set_value('Practicing') |
4109 | + >>> teacher.query.name('SUBMIT_BUTTON').click() |
4110 | + >>> print_va_section_report_table(teacher) |
4111 | + +------------------+----+----+----+---+-----+-----+----+----+----+-----+ |
4112 | + | Cerna, Camila | cc | 10 | 10 | 9 | 90% | 90% | 14 | 14 | 12 | 86% | |
4113 | + | Tejada, Mario | mt | 10 | 9 | 6 | 67% | 60% | 14 | 12 | 7 | 58% | |
4114 | + | Vividor, Liliana | | 10 | 10 | 7 | 70% | 70% | 14 | 11 | 7 | 64% | |
4115 | + +------------------+----+----+----+---+-----+-----+----+----+----+-----+ |
4116 | + +---------+---+-----+ |
4117 | + | 90-100% | 1 | 33% | |
4118 | + | 80-89% | 0 | 0% | |
4119 | + | 70-79% | 1 | 33% | |
4120 | + | 60-69% | 1 | 33% | |
4121 | + | <= 59% | 0 | 0% | |
4122 | + +---------+---+-----+ |
4123 | + +--------------+---+-----+ |
4124 | + | 80% or above | 1 | 33% | |
4125 | + | Below 80% | 2 | 67% | |
4126 | + +--------------+---+-----+ |
4127 | |
4128 | === added directory 'src/schooltool/virginia/browser/templates' |
4129 | === added file 'src/schooltool/virginia/browser/templates/section_report_charts_column.pt' |
4130 | --- src/schooltool/virginia/browser/templates/section_report_charts_column.pt 1970-01-01 00:00:00 +0000 |
4131 | +++ src/schooltool/virginia/browser/templates/section_report_charts_column.pt 2012-10-08 23:48:26 +0000 |
4132 | @@ -0,0 +1,40 @@ |
4133 | +<tal:block> |
4134 | + <script>ST.state.push()</script> |
4135 | + <tal:script |
4136 | + tal:replace="structure scriptlocal: |
4137 | + container_id options/container_id; |
4138 | + svg_size options/svg_size; |
4139 | + container_size options/container_size; |
4140 | + container_margins options/container_margins; |
4141 | + skills_count options/skills_count; |
4142 | + passing_target_size options/passing_target_size; |
4143 | + scores options/scores; |
4144 | + colors options/colors;" /> |
4145 | + <script> |
4146 | + $(document).ready(function() { |
4147 | + var container_id = ST.dialogs.jquery_id(ST.local.container_id), |
4148 | + svg_size = ST.local.svg_size, |
4149 | + container_size = ST.local.container_size, |
4150 | + container_margins = ST.local.container_margins, |
4151 | + skills_count = ST.local.skills_count, |
4152 | + passing_target_size = ST.local.passing_target_size, |
4153 | + scores = ST.local.scores, |
4154 | + colors = ST.local.colors; |
4155 | + return function(e) { |
4156 | + ST.virginia.build_chart( |
4157 | + container_id, |
4158 | + svg_size, |
4159 | + container_size, |
4160 | + container_margins, |
4161 | + skills_count, |
4162 | + passing_target_size, |
4163 | + scores, |
4164 | + colors |
4165 | + ); |
4166 | + }; |
4167 | + }()); |
4168 | + </script> |
4169 | + <script>ST.state.pop()</script> |
4170 | + <div tal:attributes="id options/container_id" class="chart-container"> |
4171 | + </div> |
4172 | +</tal:block> |
4173 | |
4174 | === added file 'src/schooltool/virginia/browser/templates/section_report_details.pt' |
4175 | --- src/schooltool/virginia/browser/templates/section_report_details.pt 1970-01-01 00:00:00 +0000 |
4176 | +++ src/schooltool/virginia/browser/templates/section_report_details.pt 2012-10-08 23:48:26 +0000 |
4177 | @@ -0,0 +1,26 @@ |
4178 | +<div class="section-report-accordion" |
4179 | + i18n:domain="schooltool.virginia"> |
4180 | + <h2 i18n:translate="">Details</h2> |
4181 | + <div> |
4182 | + <table class="form-fields"> |
4183 | + <tbody> |
4184 | + <tr> |
4185 | + <td class="label" i18n:translate="">Course</td> |
4186 | + <td tal:content="view/course/title" /> |
4187 | + </tr> |
4188 | + <tr> |
4189 | + <td class="label" i18n:translate="">Government ID</td> |
4190 | + <td tal:content="view/course/government_id" /> |
4191 | + </tr> |
4192 | + <tr> |
4193 | + <td class="label" i18n:translate="">Teachers</td> |
4194 | + <td tal:content="structure view/instructors" /> |
4195 | + </tr> |
4196 | + <tr> |
4197 | + <td class="label" i18n:translate="">Skills</td> |
4198 | + <td tal:content="python:len(view.view.skills)" /> |
4199 | + </tr> |
4200 | + </tbody> |
4201 | + </table> |
4202 | + </div> |
4203 | +</div> |
4204 | |
4205 | === added file 'src/schooltool/virginia/browser/templates/section_report_score_colors.pt' |
4206 | --- src/schooltool/virginia/browser/templates/section_report_score_colors.pt 1970-01-01 00:00:00 +0000 |
4207 | +++ src/schooltool/virginia/browser/templates/section_report_score_colors.pt 2012-10-08 23:48:26 +0000 |
4208 | @@ -0,0 +1,38 @@ |
4209 | +<tal:block i18n:domain="schooltool.virginia"> |
4210 | + <script>ST.state.push()</script> |
4211 | + <tal:script |
4212 | + tal:replace="structure scriptlocal: |
4213 | + passing_score_filter_id view/view/passing_score_filter_id; |
4214 | + colors_container_id view/view/score_colors_container_id;" |
4215 | + /> |
4216 | + <script> |
4217 | + var passing_score_filter_id = ST.local.passing_score_filter_id, |
4218 | + filter_fields = ST.virginia.get_filter_fields(), |
4219 | + passing_score_filter = $(ST.dialogs.jquery_id(passing_score_filter_id)), |
4220 | + colors_container_id = ST.local.colors_container_id; |
4221 | + passing_score_filter.on('change', function() { |
4222 | + data = ST.virginia.get_filter_field_values(filter_fields) |
4223 | + ST.table.on_form_submit(colors_container_id, null, data); |
4224 | + }); |
4225 | + </script> |
4226 | + <script>ST.state.pop()</script> |
4227 | + <div class="header" i18n:translate="">Score Colors</div> |
4228 | + <div class="body score-colors-container"> |
4229 | + <tal:block content="structure view/view/providers/ajax/score_colors_table"/> |
4230 | + <table class="score-colors-table gradient-example"> |
4231 | + <tbody> |
4232 | + <tr> |
4233 | + <td> |
4234 | + <div class="score-color-cell"> |
4235 | + <span class="color passing-target"> </span> |
4236 | + <span class="title" i18n:translate="">% Passing Target</span> |
4237 | + </div> |
4238 | + </td> |
4239 | + </tr> |
4240 | + <tr class="above-passing-target"> |
4241 | + <td i18n:translate="">Meets target</td> |
4242 | + </tr> |
4243 | + </tbody> |
4244 | + </table> |
4245 | + </div> |
4246 | +</tal:block> |
4247 | |
4248 | === added file 'src/schooltool/virginia/browser/templates/section_report_score_colors_column.pt' |
4249 | --- src/schooltool/virginia/browser/templates/section_report_score_colors_column.pt 1970-01-01 00:00:00 +0000 |
4250 | +++ src/schooltool/virginia/browser/templates/section_report_score_colors_column.pt 2012-10-08 23:48:26 +0000 |
4251 | @@ -0,0 +1,21 @@ |
4252 | +<div tal:attributes="id options/container_id" |
4253 | + class="score-color-cell"> |
4254 | + <script>ST.state.push()</script> |
4255 | + <tal:script |
4256 | + tal:replace="structure scriptlocal: |
4257 | + container_id options/container_id; |
4258 | + score options/score; |
4259 | + colors options/colors;" |
4260 | + /> |
4261 | + <script> |
4262 | + $(document).ready(function() { |
4263 | + var container_id = ST.dialogs.jquery_id(ST.local.container_id), |
4264 | + score = ST.local.score, |
4265 | + colors = ST.local.colors; |
4266 | + return function(e) { |
4267 | + ST.virginia.build_color_cell(container_id, score, colors); |
4268 | + }; |
4269 | + }()); |
4270 | + </script> |
4271 | + <script>ST.state.pop()</script> |
4272 | +</div> |
4273 | |
4274 | === added file 'src/schooltool/virginia/browser/templates/section_report_script.pt' |
4275 | --- src/schooltool/virginia/browser/templates/section_report_script.pt 1970-01-01 00:00:00 +0000 |
4276 | +++ src/schooltool/virginia/browser/templates/section_report_script.pt 2012-10-08 23:48:26 +0000 |
4277 | @@ -0,0 +1,31 @@ |
4278 | +<script>ST.state.push()</script> |
4279 | +<tal:script |
4280 | + tal:replace="structure scriptlocal: |
4281 | + form_container_id view/view/form_container_id;" |
4282 | + /> |
4283 | +<script> |
4284 | + var form_container_id = ST.local.form_container_id, |
4285 | + filter_fields = ST.virginia.get_filter_fields(), |
4286 | + filter_field, |
4287 | + event; |
4288 | + filter_fields.each(function(index, element) { |
4289 | + filter_field = $(element); |
4290 | + if (filter_field.is('select')) { |
4291 | + event = 'change'; |
4292 | + } else if (filter_field.is('input["checkbox"]')) { |
4293 | + event = 'click'; |
4294 | + }; |
4295 | + filter_field.on(event, function() { |
4296 | + data = ST.virginia.get_filter_field_values(filter_fields) |
4297 | + ST.table.on_form_submit(form_container_id, null, data); |
4298 | + }); |
4299 | + }); |
4300 | + $(document).ready(function() { |
4301 | + $('.section-report-accordion').accordion({ |
4302 | + header: 'h2', |
4303 | + active: false, |
4304 | + collapsible: true |
4305 | + }); |
4306 | + }); |
4307 | +</script> |
4308 | +<script>ST.state.pop()</script> |
4309 | |
4310 | === added file 'src/schooltool/virginia/browser/templates/va_section_report_chart.pt' |
4311 | --- src/schooltool/virginia/browser/templates/va_section_report_chart.pt 1970-01-01 00:00:00 +0000 |
4312 | +++ src/schooltool/virginia/browser/templates/va_section_report_chart.pt 2012-10-08 23:48:26 +0000 |
4313 | @@ -0,0 +1,276 @@ |
4314 | +<tal:block replace="resource_library:schooltool.virginia.flourish" /> |
4315 | +<script> |
4316 | +function buildChart(container, data, total) { |
4317 | + function set_rect_class(d, i) { |
4318 | + if (i == 0) { |
4319 | + return 'total'; |
4320 | + } else if (i == 1) { |
4321 | + return 'evaluated'; |
4322 | + } else if (i == 2) { |
4323 | + return 'rated'; |
4324 | + } |
4325 | + }; |
4326 | + function set_rect_title(d, i) { |
4327 | + if (i == 1) { |
4328 | + return 'Evaluated: '+ parseInt(d*100/total) + '%'; |
4329 | + } else if (i == 2) { |
4330 | + return 'Rated: ' + parseInt(d*100/total) + '%'; |
4331 | + } |
4332 | + }; |
4333 | + function getTicks(max, count) { |
4334 | + var delta = max/count; |
4335 | + result = []; |
4336 | + for (i=0;i<=count;i++) { |
4337 | + result.push(i*delta); |
4338 | + } |
4339 | + return result; |
4340 | + } |
4341 | + var chart_width = 250, |
4342 | + chart_height = 20, |
4343 | + ticks = getTicks(data[0], 4), |
4344 | + x = d3.scale.linear() |
4345 | + .domain([0, total]) |
4346 | + .range([0, chart_width]), |
4347 | + x_div = d3.scale.linear() |
4348 | + .domain([0, total]) |
4349 | + .range(['0px', chart_width + 'px']), |
4350 | + chart = d3.select(container).append('div') |
4351 | + .attr('class', 'chart'), |
4352 | + svg_chart = d3.select(container).append('svg') |
4353 | + .attr('class', 'svg-chart') |
4354 | + .attr('width', chart_width) |
4355 | + .attr('height', chart_height); |
4356 | + chart.selectAll("div") |
4357 | + .data(data) |
4358 | + .enter().append("div") |
4359 | + .style('width', x_div) |
4360 | + .attr('class', set_rect_class) |
4361 | + .attr('title', set_rect_title); |
4362 | + $('.chart div.total').width(chart_width-2); // borders; |
4363 | + svg_chart.selectAll("rect") |
4364 | + .data(data) |
4365 | + .enter().append("rect") |
4366 | + .attr("width", x) |
4367 | + .attr("height", chart_height) |
4368 | + .attr('class', set_rect_class) |
4369 | + .attr('title', set_rect_title); |
4370 | + svg_chart.selectAll("line") |
4371 | + .data(ticks) |
4372 | + .enter().append("line") |
4373 | + .attr("x1", x) |
4374 | + .attr("x2", x) |
4375 | + .attr("y1", 0) |
4376 | + .attr("y2", chart_height) |
4377 | + .style("stroke", "#ccc"); |
4378 | +} |
4379 | +</script> |
4380 | +<style type="text/css"> |
4381 | + th.chart-header { |
4382 | + width: 242px; |
4383 | + padding-left: 0; |
4384 | + } |
4385 | + .chart-cell { |
4386 | + padding: 0; |
4387 | + vertical-align: middle; |
4388 | + width: 250px; |
4389 | + position: relative; |
4390 | + } |
4391 | + .chart { |
4392 | + position: absolute; |
4393 | + height: 18px; |
4394 | + z-index: 1; |
4395 | + margin-top: 2px; |
4396 | + } |
4397 | + .svg-chart { |
4398 | + z-index: 0; |
4399 | + margin-top: 2px; |
4400 | + } |
4401 | + .chart div { |
4402 | + height: 18px; |
4403 | + position: absolute; |
4404 | + } |
4405 | + .chart div.total { |
4406 | + border: 1px solid #cccccc; |
4407 | + background-color: transparent; |
4408 | + opacity: 0.5; |
4409 | + } |
4410 | + .chart div.evaluated { |
4411 | + border: 1px solid transparent; |
4412 | + background-color: transparent; |
4413 | + } |
4414 | + .chart div.rated { |
4415 | + border: 1px solid transparent; |
4416 | + background-color: transparent;; |
4417 | + } |
4418 | + .svg-chart rect.total { |
4419 | + stroke: white; |
4420 | + fill: white; |
4421 | + } |
4422 | + .svg-chart rect.evaluated { |
4423 | + stroke: #E6E5E5; |
4424 | + fill: #E6E5E5; |
4425 | + } |
4426 | + .svg-chart rect.rated { |
4427 | + stroke: #008C93; |
4428 | + fill: #008C93; |
4429 | + } |
4430 | +</style> |
4431 | +<tal:block i18n:domain="schooltool.virginia" |
4432 | + tal:define="course view/course; |
4433 | + rows view/rows; |
4434 | + rows_attainment view/rows_attainment; |
4435 | + rows_attainment80 view/rows_attainment80; |
4436 | + passing_scores view/passing_scores; |
4437 | + filter_field_id view/filter_field_id; |
4438 | + scores view/scores"> |
4439 | + <table class="form-fields"> |
4440 | + <tbody> |
4441 | + <tr> |
4442 | + <td class="label" i18n:translate=""> |
4443 | + Course |
4444 | + </td> |
4445 | + <td tal:content="course/title" /> |
4446 | + </tr> |
4447 | + <tr> |
4448 | + <td class="label" i18n:translate=""> |
4449 | + Government ID |
4450 | + </td> |
4451 | + <td tal:content="course/government_id" /> |
4452 | + </tr> |
4453 | + <tr> |
4454 | + <td class="label" i18n:translate=""> |
4455 | + Teachers |
4456 | + </td> |
4457 | + <td tal:content="structure view/instructors" /> |
4458 | + </tr> |
4459 | + </tbody> |
4460 | + </table> |
4461 | + <p i18n:translate="" class="va-section-report-summary"> |
4462 | + Students will be expected to achieve a satisfactory rating on the |
4463 | + Student Competency Record (SCR) rating scale on at least 80% of |
4464 | + the required (essential) competencies in a CTE course. |
4465 | + </p> |
4466 | + <form tal:attributes="action request/URL" method="post"> |
4467 | + <fieldset> |
4468 | + <legend><span i18n:translate="">Minimum rating score</span></legend> |
4469 | + <div class="row"> |
4470 | + <div class="label"> |
4471 | + <label tal:attributes="for filter_field_id"> |
4472 | + <span i18n:translate="">Score</span> |
4473 | + </label> |
4474 | + </div> |
4475 | + <div class="widget"> |
4476 | + <select tal:attributes="id filter_field_id; |
4477 | + name filter_field_id;" |
4478 | + class="select-widget"> |
4479 | + <option tal:repeat="score scores" |
4480 | + tal:content="score/title" |
4481 | + tal:attributes="value score/id; |
4482 | + selected score/selected;" /> |
4483 | + </select> |
4484 | + </div> |
4485 | + </div> |
4486 | + </fieldset> |
4487 | + <div class="buttons"> |
4488 | + <input type="submit" name="SUBMIT_BUTTON" value="Submit" |
4489 | + class="button-ok" i18n:attributes="value" /> |
4490 | + </div> |
4491 | + </form> |
4492 | + <table> |
4493 | + <thead> |
4494 | + <tr> |
4495 | + <th i18n:translate="">Student</th> |
4496 | + <th i18n:translate="">Student ID #</th> |
4497 | + <th i18n:translate="" class="chart-header">Required Competencies</th> |
4498 | + <th class="total-skills"> </th> |
4499 | + <th i18n:translate="" class="chart-header">All Competencies</th> |
4500 | + <th class="total-skills"> </th> |
4501 | + </tr> |
4502 | + </thead> |
4503 | + <tbody> |
4504 | + <tr tal:repeat="row rows"> |
4505 | + <td tal:content="row/student" /> |
4506 | + <td tal:content="row/state_report_id" /> |
4507 | + <td class="chart-cell" |
4508 | + tal:attributes="id string:required-${row/student_id}"> |
4509 | + <script>ST.state.push()</script> |
4510 | + <tal:script |
4511 | + tal:replace="structure scriptlocal: |
4512 | + student_id row/student_id; |
4513 | + total row/required_total; |
4514 | + data row/required_data; |
4515 | + container_id row/required_container_id;" /> |
4516 | + <script> |
4517 | + var student_id = ST.local.student_id, |
4518 | + data = ST.local.data, |
4519 | + total = ST.local.total, |
4520 | + container_id = ST.dialogs.jquery_id(ST.local.container_id); |
4521 | + buildChart(container_id, data, total); |
4522 | + </script> |
4523 | + <script>ST.state.pop()</script> |
4524 | + </td> |
4525 | + <td i18n:translate=""> |
4526 | + of <tal:block i18n:name="count" content="row/required_total" /> |
4527 | + </td> |
4528 | + <td class="chart-cell" |
4529 | + tal:attributes="id string:all-${row/student_id}"> |
4530 | + <script>ST.state.push()</script> |
4531 | + <tal:script |
4532 | + tal:replace="structure scriptlocal: |
4533 | + student_id row/student_id; |
4534 | + total row/all_total; |
4535 | + data row/all_data; |
4536 | + container_id row/all_container_id;" /> |
4537 | + <script> |
4538 | + var student_id = ST.local.student_id, |
4539 | + data = ST.local.data, |
4540 | + total = ST.local.total, |
4541 | + container_id = ST.dialogs.jquery_id(ST.local.container_id); |
4542 | + buildChart(container_id, data, total); |
4543 | + </script> |
4544 | + <script>ST.state.pop()</script> |
4545 | + </td> |
4546 | + <td i18n:translate=""> |
4547 | + of <tal:block i18n:name="count" content="row/all_total" /> |
4548 | + </td> |
4549 | + </tr> |
4550 | + </tbody> |
4551 | + </table> |
4552 | + <table> |
4553 | + <thead> |
4554 | + <tr> |
4555 | + <th>Competency Attainment</th> |
4556 | + <th># Students</th> |
4557 | + <th>% of all students</th> |
4558 | + </tr> |
4559 | + </thead> |
4560 | + <tbody> |
4561 | + <tr tal:repeat="row rows_attainment"> |
4562 | + <td tal:content="row/scale" /> |
4563 | + <td tal:content="row/student_count" /> |
4564 | + <td tal:content="row/percentage" /> |
4565 | + </tr> |
4566 | + </tbody> |
4567 | + </table> |
4568 | + <table> |
4569 | + <thead> |
4570 | + <tr> |
4571 | + <th>Competency Attainment</th> |
4572 | + <th># Students</th> |
4573 | + <th>% of all students</th> |
4574 | + </tr> |
4575 | + </thead> |
4576 | + <tbody> |
4577 | + <tr tal:repeat="row rows_attainment80"> |
4578 | + <td tal:content="row/scale" /> |
4579 | + <td tal:content="row/student_count" /> |
4580 | + <td tal:content="row/percentage" /> |
4581 | + </tr> |
4582 | + </tbody> |
4583 | + </table> |
4584 | + <h3 class="done-link" i18n:domain="schooltool"> |
4585 | + <a tal:attributes="href view/next_url" i18n:translate=""> |
4586 | + Done |
4587 | + </a> |
4588 | + </h3> |
4589 | +</tal:block> |
4590 | |
4591 | === added file 'src/schooltool/virginia/browser/templates/va_section_report_details.pt' |
4592 | --- src/schooltool/virginia/browser/templates/va_section_report_details.pt 1970-01-01 00:00:00 +0000 |
4593 | +++ src/schooltool/virginia/browser/templates/va_section_report_details.pt 2012-10-08 23:48:26 +0000 |
4594 | @@ -0,0 +1,21 @@ |
4595 | +<table class="form-fields"> |
4596 | + <tbody> |
4597 | + <tr> |
4598 | + <td class="label" i18n:translate="">Course</td> |
4599 | + <td tal:content="view/course/title" /> |
4600 | + </tr> |
4601 | + <tr> |
4602 | + <td class="label" i18n:translate="">Government ID</td> |
4603 | + <td tal:content="view/course/government_id" /> |
4604 | + </tr> |
4605 | + <tr> |
4606 | + <td class="label" i18n:translate="">Teachers</td> |
4607 | + <td tal:content="structure view/instructors" /> |
4608 | + </tr> |
4609 | + </tbody> |
4610 | +</table> |
4611 | +<p i18n:translate="" class="va-section-report-summary"> |
4612 | + Students are expected to achieve a satisfactory rating (one of the |
4613 | + three highest ratings) on the Student Competency Record (SCR) rating |
4614 | + scale on at least 80% of the required (essential) competencies. |
4615 | +</p> |
4616 | |
4617 | === added file 'src/schooltool/virginia/browser/templates/va_section_report_passing_score.pt' |
4618 | --- src/schooltool/virginia/browser/templates/va_section_report_passing_score.pt 1970-01-01 00:00:00 +0000 |
4619 | +++ src/schooltool/virginia/browser/templates/va_section_report_passing_score.pt 2012-10-08 23:48:26 +0000 |
4620 | @@ -0,0 +1,28 @@ |
4621 | +<form tal:attributes="action request/URL" method="post" |
4622 | + tal:define="scores view/options; |
4623 | + passing_score_filter_id view/view/passing_score_filter_id;"> |
4624 | + <fieldset> |
4625 | + <legend><span i18n:translate="">Minimum rating score</span></legend> |
4626 | + <div class="row"> |
4627 | + <div class="label"> |
4628 | + <label tal:attributes="for passing_score_filter_id"> |
4629 | + <span i18n:translate="">Score</span> |
4630 | + </label> |
4631 | + </div> |
4632 | + <div class="widget"> |
4633 | + <select tal:attributes="id passing_score_filter_id; |
4634 | + name passing_score_filter_id;" |
4635 | + class="select-widget"> |
4636 | + <option tal:repeat="option scores" |
4637 | + tal:content="option/title" |
4638 | + tal:attributes="value option/value; |
4639 | + selected option/selected;" /> |
4640 | + </select> |
4641 | + </div> |
4642 | + </div> |
4643 | + </fieldset> |
4644 | + <div class="buttons"> |
4645 | + <input type="submit" name="SUBMIT_BUTTON" value="Submit" |
4646 | + class="button-ok" i18n:attributes="value" /> |
4647 | + </div> |
4648 | +</form> |
4649 | |
4650 | === added file 'src/schooltool/virginia/browser/templates/va_section_report_tables.pt' |
4651 | --- src/schooltool/virginia/browser/templates/va_section_report_tables.pt 1970-01-01 00:00:00 +0000 |
4652 | +++ src/schooltool/virginia/browser/templates/va_section_report_tables.pt 2012-10-08 23:48:26 +0000 |
4653 | @@ -0,0 +1,80 @@ |
4654 | +<tal:block i18n:domain="schooltool.virginia" |
4655 | + tal:define="rows view/view/rows; |
4656 | + rows_attainment view/view/rows_attainment; |
4657 | + rows_attainment80 view/view/rows_attainment80; |
4658 | + passing_scores_row view/passing_scores_row;"> |
4659 | + <table class="section-report va-section-report"> |
4660 | + <thead> |
4661 | + <tr> |
4662 | + <th rowspan="2" i18n:translate="" class="student">Student</th> |
4663 | + <th rowspan="2" i18n:translate="">Student ID #</th> |
4664 | + <th colspan="5" i18n:translate="">Required Competencies</th> |
4665 | + <th colspan="4" i18n:translate="">All Competencies</th> |
4666 | + </tr> |
4667 | + <tr> |
4668 | + <th i18n:translate="">Total</th> |
4669 | + <th i18n:translate="">Evaluated</th> |
4670 | + <th i18n:translate=""> |
4671 | + Rated<br /> |
4672 | + <tal:block content="passing_scores_row" /> |
4673 | + </th> |
4674 | + <th i18n:translate="">% Satisfactory<br />of Evaluated</th> |
4675 | + <th i18n:translate="">% Satisfactory<br />of Total</th> |
4676 | + <th i18n:translate="">Total</th> |
4677 | + <th i18n:translate="">Evaluated</th> |
4678 | + <th i18n:translate=""> |
4679 | + Rated<br /> |
4680 | + <tal:block content="passing_scores_row" /> |
4681 | + </th> |
4682 | + <th i18n:translate="">% Satisfactory<br />of Evaluated</th> |
4683 | + </tr> |
4684 | + </thead> |
4685 | + <tbody> |
4686 | + <tr tal:repeat="row rows"> |
4687 | + <td tal:content="row/student" /> |
4688 | + <td tal:content="row/student_id" /> |
4689 | + <td tal:content="row/required_total" /> |
4690 | + <td tal:content="row/required_evaluated" /> |
4691 | + <td tal:content="row/required_competent" /> |
4692 | + <td tal:content="row/required_rating" /> |
4693 | + <td tal:content="row/required_completed" /> |
4694 | + <td tal:content="row/all_total" /> |
4695 | + <td tal:content="row/all_evaluated" /> |
4696 | + <td tal:content="row/all_competent" /> |
4697 | + <td tal:content="row/all_rating" /> |
4698 | + </tr> |
4699 | + </tbody> |
4700 | + </table> |
4701 | + <table class="attainment"> |
4702 | + <thead> |
4703 | + <tr> |
4704 | + <th>Competency Attainment</th> |
4705 | + <th># Students</th> |
4706 | + <th>% of all students</th> |
4707 | + </tr> |
4708 | + </thead> |
4709 | + <tbody> |
4710 | + <tr tal:repeat="row rows_attainment"> |
4711 | + <td tal:content="row/scale" /> |
4712 | + <td tal:content="row/student_count" /> |
4713 | + <td tal:content="row/percentage" /> |
4714 | + </tr> |
4715 | + </tbody> |
4716 | + </table> |
4717 | + <table class="attainment-above80"> |
4718 | + <thead> |
4719 | + <tr> |
4720 | + <th>Competency Attainment</th> |
4721 | + <th># Students</th> |
4722 | + <th>% of all students</th> |
4723 | + </tr> |
4724 | + </thead> |
4725 | + <tbody> |
4726 | + <tr tal:repeat="row rows_attainment80"> |
4727 | + <td tal:content="row/scale" /> |
4728 | + <td tal:content="row/student_count" /> |
4729 | + <td tal:content="row/percentage" /> |
4730 | + </tr> |
4731 | + </tbody> |
4732 | + </table> |
4733 | +</tal:block> |
4734 | |
4735 | === added file 'src/schooltool/virginia/configure.zcml' |
4736 | --- src/schooltool/virginia/configure.zcml 1970-01-01 00:00:00 +0000 |
4737 | +++ src/schooltool/virginia/configure.zcml 2012-10-08 23:48:26 +0000 |
4738 | @@ -0,0 +1,18 @@ |
4739 | +<?xml version="1.0"?> |
4740 | +<configure xmlns="http://namespaces.zope.org/zope" |
4741 | + xmlns:i18n="http://namespaces.zope.org/i18n"> |
4742 | + |
4743 | + <adapter |
4744 | + factory=".virginia.VirginiaInit" |
4745 | + name="schooltool.virginia" /> |
4746 | + |
4747 | + <adapter |
4748 | + factory=".virginia.VirginiaAppStartup" |
4749 | + name="schooltool.virginia" /> |
4750 | + |
4751 | + <include package=".browser" /> |
4752 | + |
4753 | + <i18n:registerTranslations directory="locales" /> |
4754 | + |
4755 | +</configure> |
4756 | + |
4757 | |
4758 | === added directory 'src/schooltool/virginia/locales' |
4759 | === added file 'src/schooltool/virginia/locales/__init__.py' |
4760 | --- src/schooltool/virginia/locales/__init__.py 1970-01-01 00:00:00 +0000 |
4761 | +++ src/schooltool/virginia/locales/__init__.py 2012-10-08 23:48:26 +0000 |
4762 | @@ -0,0 +1,1 @@ |
4763 | +# Make a package |
4764 | |
4765 | === added file 'src/schooltool/virginia/locales/en.po' |
4766 | --- src/schooltool/virginia/locales/en.po 1970-01-01 00:00:00 +0000 |
4767 | +++ src/schooltool/virginia/locales/en.po 2012-10-08 23:48:26 +0000 |
4768 | @@ -0,0 +1,29 @@ |
4769 | +# SchoolTool - common information systems platform for school administration |
4770 | +# Copyright (c) 2007 Shuttleworth Foundation |
4771 | +# |
4772 | +# This program is free software; you can redistribute it and/or modify |
4773 | +# it under the terms of the GNU General Public License as published by |
4774 | +# the Free Software Foundation; either version 2 of the License, or |
4775 | +# (at your option) any later version. |
4776 | +# |
4777 | +# This program is distributed in the hope that it will be useful, |
4778 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4779 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4780 | +# GNU General Public License for more details. |
4781 | +# |
4782 | +# You should have received a copy of the GNU General Public License |
4783 | +# along with this program; if not, write to the Free Software |
4784 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
4785 | +msgid "" |
4786 | +msgstr "" |
4787 | +"Project-Id-Version: schooltool.virgina\n" |
4788 | +"POT-Creation-Date: 2012-02-04 10:17-0500\n" |
4789 | +"PO-Revision-Date: 2012-02-04 10:18-0500\n" |
4790 | +"Last-Translator: justas <justas@pov.lt>\n" |
4791 | +"Language-Team: English\n" |
4792 | +"Language: en\n" |
4793 | +"MIME-Version: 1.0\n" |
4794 | +"Content-Type: text/plain; charset=UTF-8\n" |
4795 | +"Content-Transfer-Encoding: 8bit\n" |
4796 | +"Generated-By: i18nextract.py\n" |
4797 | +"Plural-Forms: nplurals=2; plural=(n != 1);\n" |
4798 | |
4799 | === added file 'src/schooltool/virginia/locales/schooltool.virginia.pot' |
4800 | --- src/schooltool/virginia/locales/schooltool.virginia.pot 1970-01-01 00:00:00 +0000 |
4801 | +++ src/schooltool/virginia/locales/schooltool.virginia.pot 2012-10-08 23:48:26 +0000 |
4802 | @@ -0,0 +1,28 @@ |
4803 | +# SchoolTool - common information systems platform for school administration |
4804 | +# Copyright (c) 2007 Shuttleworth Foundation |
4805 | +# |
4806 | +# This program is free software; you can redistribute it and/or modify |
4807 | +# it under the terms of the GNU General Public License as published by |
4808 | +# the Free Software Foundation; either version 2 of the License, or |
4809 | +# (at your option) any later version. |
4810 | +# |
4811 | +# This program is distributed in the hope that it will be useful, |
4812 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
4813 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4814 | +# GNU General Public License for more details. |
4815 | +# |
4816 | +# You should have received a copy of the GNU General Public License |
4817 | +# along with this program; if not, write to the Free Software |
4818 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
4819 | +msgid "" |
4820 | +msgstr "" |
4821 | +"Project-Id-Version: schooltool.virgina\n" |
4822 | +"POT-Creation-Date: 2012-02-04 10:17-0500\n" |
4823 | +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
4824 | +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
4825 | +"Language-Team: Schooltool Development Team <schooltool-dev@schooltool.org>\n" |
4826 | +"MIME-Version: 1.0\n" |
4827 | +"Content-Type: text/plain; charset=UTF-8\n" |
4828 | +"Content-Transfer-Encoding: 8bit\n" |
4829 | +"Generated-By: i18nextract.py\n" |
4830 | + |
4831 | |
4832 | === added file 'src/schooltool/virginia/plugin.zcml' |
4833 | --- src/schooltool/virginia/plugin.zcml 1970-01-01 00:00:00 +0000 |
4834 | +++ src/schooltool/virginia/plugin.zcml 2012-10-08 23:48:26 +0000 |
4835 | @@ -0,0 +1,6 @@ |
4836 | +<?xml version="1.0"?> |
4837 | +<configure xmlns="http://namespaces.zope.org/zope"> |
4838 | + <!-- This is an entry point for automatic plugin inclusion --> |
4839 | + <include package="." /> |
4840 | +</configure> |
4841 | + |
4842 | |
4843 | === added directory 'src/schooltool/virginia/scripts' |
4844 | === added file 'src/schooltool/virginia/scripts/getverso_xls.py' |
4845 | --- src/schooltool/virginia/scripts/getverso_xls.py 1970-01-01 00:00:00 +0000 |
4846 | +++ src/schooltool/virginia/scripts/getverso_xls.py 2012-10-08 23:48:26 +0000 |
4847 | @@ -0,0 +1,708 @@ |
4848 | +#!/usr/bin/python |
4849 | + |
4850 | +import sys |
4851 | +import urllib |
4852 | +import json |
4853 | +import cPickle as pickle |
4854 | +import xlwt |
4855 | + |
4856 | +VERSO_URL = 'http://api.cteresource.org' |
4857 | + |
4858 | + |
4859 | +class DB(dict): |
4860 | + |
4861 | + filename = None |
4862 | + _changed = 1 |
4863 | + _valid = False |
4864 | + _savepoint_every = 1000 |
4865 | + |
4866 | + def __init__(self, filename): |
4867 | + dict.__init__(self) |
4868 | + self.filename = filename |
4869 | + |
4870 | + def __setitem__(self, *args, **kw): |
4871 | + self._changed += 1 |
4872 | + if self._changed % self._savepoint_every == 0: |
4873 | + self.backup() |
4874 | + self.save() |
4875 | + dict.__setitem__(self, *args, **kw) |
4876 | + |
4877 | + def load(self, quiet=False): |
4878 | + print 'Loading...' |
4879 | + self.clear() |
4880 | + try: |
4881 | + f = open(self.filename) |
4882 | + except IOError: |
4883 | + if not quiet: |
4884 | + print 'Failed to load', self.filename |
4885 | + return False |
4886 | + try: |
4887 | + data = pickle.load(f) |
4888 | + except EOFError: |
4889 | + print 'Failed to load', self.filename |
4890 | + return False |
4891 | + self.update(data) |
4892 | + f.close() |
4893 | + self._valid = True |
4894 | + return True |
4895 | + |
4896 | + def backup(self): |
4897 | + if not self._valid: |
4898 | + return |
4899 | + try: |
4900 | + db = open(self.filename) |
4901 | + except IOError: |
4902 | + return |
4903 | + backup_name = self.filename+'.bak' |
4904 | + print 'Making backup', backup_name |
4905 | + backup = open(backup_name, 'w') |
4906 | + backup.write(db.read()) |
4907 | + db.close() |
4908 | + backup.close() |
4909 | + |
4910 | + def save(self, filename=None): |
4911 | + if filename is None: |
4912 | + filename = self.filename |
4913 | + f = open(filename, 'w') |
4914 | + print 'Saving', filename |
4915 | + pickle.dump(dict(self), f) |
4916 | + f.close() |
4917 | + self._valid = True |
4918 | + |
4919 | + |
4920 | +LOADED = DB('verso-db') |
4921 | + |
4922 | + |
4923 | +class NotFound(Exception): |
4924 | + pass |
4925 | + |
4926 | + |
4927 | +def retrieve(url): |
4928 | + while url.startswith('/'): |
4929 | + url = url[1:] |
4930 | + url = VERSO_URL+'/'+url |
4931 | + f = urllib.urlopen(url) |
4932 | + data = json.load(f) |
4933 | + f.close() |
4934 | + if 'errors' in data: |
4935 | + if data['errors'] == 'Not Found': |
4936 | + raise NotFound(url) |
4937 | + raise Exception(data['errors'], url) |
4938 | + return data |
4939 | + |
4940 | + |
4941 | +class Doodad(object): |
4942 | + |
4943 | + def __init__(self, **kw): |
4944 | + self.raw_data = {} |
4945 | + if kw: |
4946 | + self.update(kw) |
4947 | + |
4948 | + @classmethod |
4949 | + def query(cls, *args, **kw): |
4950 | + doodad = cls(*args, **kw) |
4951 | + return LOADED.get(doodad.unique_key, doodad) |
4952 | + |
4953 | + |
4954 | + @property |
4955 | + def is_loaded(self): |
4956 | + return self.unique_key in LOADED |
4957 | + |
4958 | + @property |
4959 | + def unique_key(self): |
4960 | + return self.__class__.__name__, self.id |
4961 | + |
4962 | + def __getattr__(self, name): |
4963 | + if name.startswith('__'): |
4964 | + return object.__getattr__(self, name) |
4965 | + if not hasattr(self, 'id'): |
4966 | + raise Exception('Cannot load object without id') |
4967 | + global LOADED |
4968 | + key = self.unique_key |
4969 | + if key not in LOADED: |
4970 | + self.load() |
4971 | + if key not in LOADED: |
4972 | + raise AttributeError(name) |
4973 | + return getattr(LOADED[key], name) |
4974 | + |
4975 | + def load(self): |
4976 | + key = self.unique_key |
4977 | + print 'LOADING', key |
4978 | + LOADED[key] = self |
4979 | + cn = self.__class__.__name__ |
4980 | + if cn not in BY_CLASS: |
4981 | + BY_CLASS[cn] = {} |
4982 | + BY_CLASS[cn][key] = self |
4983 | + |
4984 | + def update(self, data, soft=True): |
4985 | + if soft: |
4986 | + raw = self.raw_data |
4987 | + for k, v in data.items(): |
4988 | + if k not in raw: |
4989 | + raw[k] = v |
4990 | + else: |
4991 | + self.raw_data.update(data) |
4992 | + for key in self.raw_data: |
4993 | + try: |
4994 | + setattr(self, key, self.raw_data[key]) |
4995 | + except: |
4996 | + pass # meh |
4997 | + |
4998 | + def __repr__(self): |
4999 | + return '<%s %s %r>' % (self.__class__.__name__, |
5000 | + self.id, |