groonga-query-log 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. data/.yardopts +5 -0
  2. data/Gemfile +21 -0
  3. data/README.md +49 -0
  4. data/Rakefile +51 -0
  5. data/bin/groonga-query-log-analyzer +28 -0
  6. data/doc/text/lgpl-2.1.txt +502 -0
  7. data/doc/text/news.md +5 -0
  8. data/groonga-query-log.gemspec +65 -0
  9. data/lib/groonga/query-log.rb +21 -0
  10. data/lib/groonga/query-log/analyzer.rb +212 -0
  11. data/lib/groonga/query-log/analyzer/reporter.rb +101 -0
  12. data/lib/groonga/query-log/analyzer/reporter/console.rb +291 -0
  13. data/lib/groonga/query-log/analyzer/reporter/html.rb +325 -0
  14. data/lib/groonga/query-log/analyzer/reporter/json.rb +78 -0
  15. data/lib/groonga/query-log/analyzer/sized-grouped-operations.rb +84 -0
  16. data/lib/groonga/query-log/analyzer/sized-statistics.rb +172 -0
  17. data/lib/groonga/query-log/analyzer/statistic.rb +160 -0
  18. data/lib/groonga/query-log/analyzer/streamer.rb +42 -0
  19. data/lib/groonga/query-log/parser.rb +77 -0
  20. data/lib/groonga/query-log/version.rb +23 -0
  21. data/test/command/test-select.rb +162 -0
  22. data/test/fixtures/n_entries.expected +19 -0
  23. data/test/fixtures/no-report-summary.expected +15 -0
  24. data/test/fixtures/order/-elapsed.expected +28 -0
  25. data/test/fixtures/order/-start-time.expected +28 -0
  26. data/test/fixtures/order/elapsed.expected +28 -0
  27. data/test/fixtures/order/start-time.expected +28 -0
  28. data/test/fixtures/query.log +7 -0
  29. data/test/fixtures/reporter/console.expected +28 -0
  30. data/test/fixtures/reporter/html.expected +196 -0
  31. data/test/fixtures/reporter/json.expected +4 -0
  32. data/test/groonga-query-log-test-utils.rb +79 -0
  33. data/test/run-test.rb +43 -0
  34. data/test/test-analyzer.rb +82 -0
  35. data/test/test-parser.rb +90 -0
  36. metadata +235 -0
@@ -0,0 +1,23 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2012 Kouhei Sutou <kou@clear-code.com>
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+
19
+ module Groonga
20
+ module QueryLog
21
+ VERSION = "1.0.0"
22
+ end
23
+ end
@@ -0,0 +1,162 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright (C) 2011-2012 Kouhei Sutou <kou@clear-code.com>
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+
19
+ class SelectCommandTest < Test::Unit::TestCase
20
+ module ParseTests
21
+ def test_parameters
22
+ select = parse("select",
23
+ :table => "Users",
24
+ :filter => "age<=30")
25
+ assert_equal(command("select",
26
+ "table" => "Users",
27
+ "filter" => "age<=30",
28
+ "output_type" => "json"),
29
+ select)
30
+ end
31
+
32
+ def test_scorer
33
+ select = parse("select",
34
+ :table => "Users",
35
+ :filter => "age<=30",
36
+ :scorer => "_score = random()")
37
+ assert_equal("_score = random()", select.scorer)
38
+ end
39
+
40
+ def test_to_uri_format
41
+ select = parse("select",
42
+ :table => "Users",
43
+ :filter => "age<=30")
44
+ assert_equal("/d/select.json?filter=age%3C%3D30&table=Users",
45
+ select.to_uri_format)
46
+ end
47
+
48
+ def test_to_command_format
49
+ select = parse("select",
50
+ :table => "Users",
51
+ :filter => "age<=30")
52
+ assert_equal("select --filter \"age<=30\" " +
53
+ "--output_type \"json\" --table \"Users\"",
54
+ select.to_command_format)
55
+ end
56
+ end
57
+
58
+ module ParseFilterTests
59
+ def test_parenthesis
60
+ filter = 'geo_in_rectangle(location,' +
61
+ '"35.73360x139.7394","62614x139.7714") && ' +
62
+ '((type == "たいやき" || type == "和菓子")) && ' +
63
+ 'keyword @ "たいやき" &! keyword @ "白" &! keyword @ "養殖"'
64
+ select = parse("select",
65
+ :table => "Users",
66
+ :filter => filter)
67
+ assert_equal(['geo_in_rectangle(location,' +
68
+ '"35.73360x139.7394","62614x139.7714")',
69
+ 'type == "たいやき"',
70
+ 'type == "和菓子"',
71
+ 'keyword @ "たいやき"',
72
+ 'keyword @ "白"',
73
+ 'keyword @ "養殖"'],
74
+ select.conditions)
75
+ end
76
+
77
+ def test_to_uri_format
78
+ filter = 'geo_in_rectangle(location,' +
79
+ '"35.73360x139.7394","62614x139.7714") && ' +
80
+ '((type == "たいやき" || type == "和菓子")) && ' +
81
+ 'keyword @ "たいやき" &! keyword @ "白" &! keyword @ "養殖"'
82
+ select = parse("select",
83
+ :table => "Users",
84
+ :filter => filter)
85
+ assert_equal("/d/select.json?filter=geo_in_rectangle%28location%2C" +
86
+ "%2235.73360x139.7394%22%2C%2262614x139.7714%22%29+" +
87
+ "%26%26+%28%28type+" +
88
+ "%3D%3D+%22%E3%81%9F%E3%81%84%E3%82%84%E3%81%8D%22+" +
89
+ "%7C%7C+type+%3D%3D+" +
90
+ "%22%E5%92%8C%E8%8F%93%E5%AD%90%22%29%29+" +
91
+ "%26%26+keyword+%40+" +
92
+ "%22%E3%81%9F%E3%81%84%E3%82%84%E3%81%8D%22+%26%21+" +
93
+ "keyword+%40+%22%E7%99%BD%22+%26%21+" +
94
+ "keyword+%40+%22%E9%A4%8A%E6%AE%96%22&table=Users",
95
+ select.to_uri_format)
96
+ end
97
+
98
+ def test_to_command_format
99
+ filter = 'geo_in_rectangle(location,' +
100
+ '"35.73360x139.7394","62614x139.7714") && ' +
101
+ '((type == "たいやき" || type == "和菓子")) && ' +
102
+ 'keyword @ "たいやき" &! keyword @ "白" &! keyword @ "養殖"'
103
+ select = parse("select",
104
+ :table => "Users",
105
+ :filter => filter)
106
+ assert_equal("select " +
107
+ "--filter " +
108
+ "\"geo_in_rectangle(location," +
109
+ "\\\"35.73360x139.7394\\\",\\\"62614x139.7714\\\") && " +
110
+ "((type == \\\"たいやき\\\" || " +
111
+ "type == \\\"和菓子\\\")) && " +
112
+ "keyword @ \\\"たいやき\\\" &! keyword @ \\\"白\\\" &! " +
113
+ "keyword @ \\\"養殖\\\"\" " +
114
+ "--output_type \"json\" --table \"Users\"",
115
+ select.to_command_format)
116
+ end
117
+ end
118
+
119
+ class HTTPTest < self
120
+ include GroongaQueryLogTestUtils::HTTPCommandParser
121
+
122
+ def test_uri_format?
123
+ command = parse("status")
124
+ assert_predicate(command, :uri_format?)
125
+ end
126
+
127
+ def test_command_format?
128
+ command = parse("status")
129
+ assert_not_predicate(command, :command_format?)
130
+ end
131
+
132
+ class ParseTest < self
133
+ include ParseTests
134
+ end
135
+
136
+ class ParseFilterTest < self
137
+ include ParseFilterTests
138
+ end
139
+ end
140
+
141
+ class CommandLineTest < self
142
+ include GroongaQueryLogTestUtils::CommandLineCommandParser
143
+
144
+ def test_uri_format?
145
+ command = parse("status")
146
+ assert_not_predicate(command, :uri_format?)
147
+ end
148
+
149
+ def test_command_format?
150
+ command = parse("status")
151
+ assert_predicate(command, :command_format?)
152
+ end
153
+
154
+ class ParseTest < self
155
+ include ParseTests
156
+ end
157
+
158
+ class ParseFilterTest < self
159
+ include ParseFilterTests
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,19 @@
1
+ Summary:
2
+ Threshold:
3
+ slow response : 0.2
4
+ slow operation : 0.1
5
+ # of responses : 2
6
+ # of slow responses : 0
7
+ responses/sec : 387.0613143828114
8
+ start time : 2012-12-12 17:39:17.3
9
+ last time : 2012-12-12 17:39:17.3
10
+ period(sec) : 0.00516714
11
+ slow response ratio : 0.000%
12
+ total response time : 0.004345996
13
+ Slow Operations:
14
+
15
+ Slow Queries:
16
+ 1) [2012-12-12 17:39:17.3-2012-12-12 17:39:17.3 (0.00312886)](0): load --table Video name: <load>
17
+ parameters:
18
+ <table>: <Video>
19
+
@@ -0,0 +1,15 @@
1
+
2
+ Slow Queries:
3
+ 1) [2012-12-12 17:39:17.3-2012-12-12 17:39:17.3 (0.00312886)](0): load --table Video name: <load>
4
+ parameters:
5
+ <table>: <Video>
6
+
7
+ 2) [2012-12-12 17:39:17.3-2012-12-12 17:39:17.3 (0.00121714)](0): select --table Users --query follower:@groonga --output_columns _key,name name: <select>
8
+ parameters:
9
+ <table>: <Users>
10
+ <query>: <follower:@groonga>
11
+ <output_columns>: <_key,name>
12
+ 1) 0.00084295: filter( 2) query: follower:@groonga
13
+ 2) 0.00002795: select( 2)
14
+ 3) 0.00019585: output( 2) _key,name
15
+
@@ -0,0 +1,28 @@
1
+ Summary:
2
+ Threshold:
3
+ slow response : 0.2
4
+ slow operation : 0.1
5
+ # of responses : 2
6
+ # of slow responses : 0
7
+ responses/sec : 387.0613143828114
8
+ start time : 2012-12-12 17:39:17.3
9
+ last time : 2012-12-12 17:39:17.3
10
+ period(sec) : 0.00516714
11
+ slow response ratio : 0.000%
12
+ total response time : 0.004345996
13
+ Slow Operations:
14
+
15
+ Slow Queries:
16
+ 1) [2012-12-12 17:39:17.3-2012-12-12 17:39:17.3 (0.00312886)](0): load --table Video name: <load>
17
+ parameters:
18
+ <table>: <Video>
19
+
20
+ 2) [2012-12-12 17:39:17.3-2012-12-12 17:39:17.3 (0.00121714)](0): select --table Users --query follower:@groonga --output_columns _key,name name: <select>
21
+ parameters:
22
+ <table>: <Users>
23
+ <query>: <follower:@groonga>
24
+ <output_columns>: <_key,name>
25
+ 1) 0.00084295: filter( 2) query: follower:@groonga
26
+ 2) 0.00002795: select( 2)
27
+ 3) 0.00019585: output( 2) _key,name
28
+
@@ -0,0 +1,28 @@
1
+ Summary:
2
+ Threshold:
3
+ slow response : 0.2
4
+ slow operation : 0.1
5
+ # of responses : 2
6
+ # of slow responses : 0
7
+ responses/sec : 387.0613143828114
8
+ start time : 2012-12-12 17:39:17.3
9
+ last time : 2012-12-12 17:39:17.3
10
+ period(sec) : 0.00516714
11
+ slow response ratio : 0.000%
12
+ total response time : 0.004345996
13
+ Slow Operations:
14
+
15
+ Slow Queries:
16
+ 1) [2012-12-12 17:39:17.3-2012-12-12 17:39:17.3 (0.00121714)](0): select --table Users --query follower:@groonga --output_columns _key,name name: <select>
17
+ parameters:
18
+ <table>: <Users>
19
+ <query>: <follower:@groonga>
20
+ <output_columns>: <_key,name>
21
+ 1) 0.00084295: filter( 2) query: follower:@groonga
22
+ 2) 0.00002795: select( 2)
23
+ 3) 0.00019585: output( 2) _key,name
24
+
25
+ 2) [2012-12-12 17:39:17.3-2012-12-12 17:39:17.3 (0.00312886)](0): load --table Video name: <load>
26
+ parameters:
27
+ <table>: <Video>
28
+
@@ -0,0 +1,28 @@
1
+ Summary:
2
+ Threshold:
3
+ slow response : 0.2
4
+ slow operation : 0.1
5
+ # of responses : 2
6
+ # of slow responses : 0
7
+ responses/sec : 387.0613143828114
8
+ start time : 2012-12-12 17:39:17.3
9
+ last time : 2012-12-12 17:39:17.3
10
+ period(sec) : 0.00516714
11
+ slow response ratio : 0.000%
12
+ total response time : 0.004345996
13
+ Slow Operations:
14
+
15
+ Slow Queries:
16
+ 1) [2012-12-12 17:39:17.3-2012-12-12 17:39:17.3 (0.00121714)](0): select --table Users --query follower:@groonga --output_columns _key,name name: <select>
17
+ parameters:
18
+ <table>: <Users>
19
+ <query>: <follower:@groonga>
20
+ <output_columns>: <_key,name>
21
+ 1) 0.00084295: filter( 2) query: follower:@groonga
22
+ 2) 0.00002795: select( 2)
23
+ 3) 0.00019585: output( 2) _key,name
24
+
25
+ 2) [2012-12-12 17:39:17.3-2012-12-12 17:39:17.3 (0.00312886)](0): load --table Video name: <load>
26
+ parameters:
27
+ <table>: <Video>
28
+
@@ -0,0 +1,28 @@
1
+ Summary:
2
+ Threshold:
3
+ slow response : 0.2
4
+ slow operation : 0.1
5
+ # of responses : 2
6
+ # of slow responses : 0
7
+ responses/sec : 387.0613143828114
8
+ start time : 2012-12-12 17:39:17.3
9
+ last time : 2012-12-12 17:39:17.3
10
+ period(sec) : 0.00516714
11
+ slow response ratio : 0.000%
12
+ total response time : 0.004345996
13
+ Slow Operations:
14
+
15
+ Slow Queries:
16
+ 1) [2012-12-12 17:39:17.3-2012-12-12 17:39:17.3 (0.00312886)](0): load --table Video name: <load>
17
+ parameters:
18
+ <table>: <Video>
19
+
20
+ 2) [2012-12-12 17:39:17.3-2012-12-12 17:39:17.3 (0.00121714)](0): select --table Users --query follower:@groonga --output_columns _key,name name: <select>
21
+ parameters:
22
+ <table>: <Users>
23
+ <query>: <follower:@groonga>
24
+ <output_columns>: <_key,name>
25
+ 1) 0.00084295: filter( 2) query: follower:@groonga
26
+ 2) 0.00002795: select( 2)
27
+ 3) 0.00019585: output( 2) _key,name
28
+
@@ -0,0 +1,7 @@
1
+ 2012-12-12 17:39:17.624896|0x7fffaac6dcf0|>load --table Video
2
+ 2012-12-12 17:39:17.628002|0x7fffaac6dcf0|<000000003128856 rc=0
3
+ 2012-12-12 17:39:17.628846|0x7fff786aa2b0|>select --table Users --query follower:@groonga --output_columns _key,name
4
+ 2012-12-12 17:39:17.629676|0x7fff786aa2b0|:000000000842953 filter(2)
5
+ 2012-12-12 17:39:17.629709|0x7fff786aa2b0|:000000000870900 select(2)
6
+ 2012-12-12 17:39:17.629901|0x7fff786aa2b0|:000000001066752 output(2)
7
+ 2012-12-12 17:39:17.630052|0x7fff786aa2b0|<000000001217140 rc=0
@@ -0,0 +1,28 @@
1
+ Summary:
2
+ Threshold:
3
+ slow response : 0.2
4
+ slow operation : 0.1
5
+ # of responses : 2
6
+ # of slow responses : 0
7
+ responses/sec : 387.0613143828114
8
+ start time : 2012-12-12 17:39:17.3
9
+ last time : 2012-12-12 17:39:17.3
10
+ period(sec) : 0.00516714
11
+ slow response ratio : 0.000%
12
+ total response time : 0.004345996
13
+ Slow Operations:
14
+
15
+ Slow Queries:
16
+ 1) [2012-12-12 17:39:17.3-2012-12-12 17:39:17.3 (0.00312886)](0): load --table Video name: <load>
17
+ parameters:
18
+ <table>: <Video>
19
+
20
+ 2) [2012-12-12 17:39:17.3-2012-12-12 17:39:17.3 (0.00121714)](0): select --table Users --query follower:@groonga --output_columns _key,name name: <select>
21
+ parameters:
22
+ <table>: <Users>
23
+ <query>: <follower:@groonga>
24
+ <output_columns>: <_key,name>
25
+ 1) 0.00084295: filter( 2) query: follower:@groonga
26
+ 2) 0.00002795: select( 2)
27
+ 3) 0.00019585: output( 2) _key,name
28
+
@@ -0,0 +1,196 @@
1
+ <html>
2
+ <head>
3
+ <title>groonga query analyzer</title>
4
+ <style>
5
+ table,
6
+ table tr,
7
+ table tr th,
8
+ table tr td
9
+ {
10
+ border: 1px solid black;
11
+ }
12
+
13
+ span.slow
14
+ {
15
+ color: red;
16
+ }
17
+
18
+ div.parameters
19
+ {
20
+ float: left;
21
+ padding: 2em;
22
+ }
23
+
24
+ div.parameters h3
25
+ {
26
+ text-align: center;
27
+ }
28
+
29
+ div.parameters table
30
+ {
31
+ margin-right: auto;
32
+ margin-left: auto;
33
+ }
34
+
35
+ div.statistics
36
+ {
37
+ clear: both;
38
+ }
39
+
40
+ td.elapsed,
41
+ td.ratio,
42
+ td.n
43
+ {
44
+ text-align: right;
45
+ }
46
+
47
+ td.name
48
+ {
49
+ text-align: center;
50
+ }
51
+ </style>
52
+ </head>
53
+ <body>
54
+ <h1>groonga query analyzer</h1>
55
+ <h2>Summary</h2>
56
+ <div class="summary">
57
+ <div class="parameters">
58
+ <h3>Analyze Parameters</h3>
59
+ <table>
60
+ <tr><th>Name</th><th>Value</th></tr>
61
+ <tr>
62
+ <th>Slow response threshold</th>
63
+ <td>0.2sec</td>
64
+ </tr>
65
+ <tr>
66
+ <th>Slow operation threshold</th>
67
+ <td>0.1sec</td>
68
+ </tr>
69
+ </table>
70
+ </div>
71
+ <div class="parameters">
72
+ <h3>Metrics</h3>
73
+ <table>
74
+ <tr><th>Name</th><th>Value</th></tr>
75
+ <tr>
76
+ <th># of responses</th>
77
+ <td>2</td>
78
+ </tr>
79
+ <tr>
80
+ <th># of slow responses</th>
81
+ <td>0</td>
82
+ </tr>
83
+ <tr>
84
+ <th>responses/sec</th>
85
+ <td>387.0613143828114</td>
86
+ </tr>
87
+ <tr>
88
+ <th>start time</th>
89
+ <td><span class="time">2012-12-12 17:39:17.3</span></td>
90
+ </tr>
91
+ <tr>
92
+ <th>last time</th>
93
+ <td><span class="time">2012-12-12 17:39:17.3</span></td>
94
+ </tr>
95
+ <tr>
96
+ <th>period</th>
97
+ <td>0.00516714sec</td>
98
+ </tr>
99
+ <tr>
100
+ <th>slow response ratio</th>
101
+ <td>0.0%</td>
102
+ </tr>
103
+ <tr>
104
+ <th>total response time</th>
105
+ <td>0.004345996sec</td>
106
+ </tr>
107
+ </table>
108
+ </div>
109
+ <div class="statistics">
110
+ <h3>Slow Operations</h3>
111
+ <table class="slow-operations">
112
+ <tr>
113
+ <th>total elapsed(sec)</th>
114
+ <th>total elapsed(%)</th>
115
+ <th># of operations</th>
116
+ <th># of operations(%)</th>
117
+ <th>operation name</th>
118
+ <th>context</th>
119
+ </tr>
120
+ </table>
121
+ </div>
122
+ </div>
123
+ <h2>Slow Queries</h2>
124
+ <div>
125
+ <div class="statistic-heading">
126
+ <h3>Command</h3>
127
+ <div class="metrics">
128
+ [<span class="time">2012-12-12 17:39:17.3</span>
129
+ -
130
+ <span class="time">2012-12-12 17:39:17.3</span>
131
+ (<span class="elapsed">0.00312886</span>)]
132
+ (<span class="return-code">0</span>)
133
+ </div>
134
+ <div class="raw-command">load --table Video</div>
135
+ </div>
136
+ <div class="statistic-parameters">
137
+ <h3>Parameters</h3>
138
+ <dl>
139
+ <dt>name</dt>
140
+ <dd>load</dd>
141
+ <dt>table</dt>
142
+ <dd>Video</dd>
143
+ </dl>
144
+ </div>
145
+ <div class="statistic-operations">
146
+ <h3>Operations</h3>
147
+ <ol>
148
+ </ol>
149
+ </div>
150
+ <div class="statistic-heading">
151
+ <h3>Command</h3>
152
+ <div class="metrics">
153
+ [<span class="time">2012-12-12 17:39:17.3</span>
154
+ -
155
+ <span class="time">2012-12-12 17:39:17.3</span>
156
+ (<span class="elapsed">0.00121714</span>)]
157
+ (<span class="return-code">0</span>)
158
+ </div>
159
+ <div class="raw-command">select --table Users --query follower:@groonga --output_columns _key,name</div>
160
+ </div>
161
+ <div class="statistic-parameters">
162
+ <h3>Parameters</h3>
163
+ <dl>
164
+ <dt>name</dt>
165
+ <dd>select</dd>
166
+ <dt>table</dt>
167
+ <dd>Users</dd>
168
+ <dt>query</dt>
169
+ <dd>follower:@groonga</dd>
170
+ <dt>output_columns</dt>
171
+ <dd>_key,name</dd>
172
+ </dl>
173
+ </div>
174
+ <div class="statistic-operations">
175
+ <h3>Operations</h3>
176
+ <ol>
177
+ <li>
178
+ <span class="elapsed">0.00084295</span>:
179
+ <span class="name">filter</span>:
180
+ <span class="context">query: follower:@groonga</span>
181
+ </li>
182
+ <li>
183
+ <span class="elapsed">0.00002795</span>:
184
+ <span class="name">select</span>:
185
+ <span class="context"></span>
186
+ </li>
187
+ <li>
188
+ <span class="elapsed">0.00019585</span>:
189
+ <span class="name">output</span>:
190
+ <span class="context">_key,name</span>
191
+ </li>
192
+ </ol>
193
+ </div>
194
+ </div>
195
+ </body>
196
+ </html>