Merge lp:~replaceafill/schooltool/schooltool.virginia_done_links into lp:schooltool/flourish

Proposed by Douglas Cerna
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
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.

To post a comment you must log in.

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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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">&nbsp;</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">&nbsp;</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">&nbsp;</th>
4499+ <th i18n:translate="" class="chart-header">All Competencies</th>
4500+ <th class="total-skills">&nbsp;</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,
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches