aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorhalcy <halcy@ARARAGI-KUN>2022-11-27 02:43:22 +0200
committerhalcy <halcy@ARARAGI-KUN>2022-11-27 02:43:22 +0200
commit5cf0fa27a95f8c56280ea429dd1d57529fc490ee (patch)
tree26e4485d4e69052bc53c151ba807b8260185a419 /tests
parent7331f7774ae10d927e086318338989de6bc37cad (diff)
downloadmastodon.py-5cf0fa27a95f8c56280ea429dd1d57529fc490ee.tar.gz
add admin stats APIs
Diffstat (limited to 'tests')
-rw-r--r--tests/cassettes/test_admin_stats.yaml201
-rw-r--r--tests/setup.sql5
-rw-r--r--tests/test_admin.py48
3 files changed, 254 insertions, 0 deletions
diff --git a/tests/cassettes/test_admin_stats.yaml b/tests/cassettes/test_admin_stats.yaml
new file mode 100644
index 0000000..dc88b9b
--- /dev/null
+++ b/tests/cassettes/test_admin_stats.yaml
@@ -0,0 +1,201 @@
1interactions:
2- request:
3 body: '{"instance_accounts": {"domain": "chitter.xyz"}, "instance_media_attachments":
4 {"domain": "chitter.xyz"}, "instance_reports": {"domain": "chitter.xyz"}, "instance_statuses":
5 {"domain": "chitter.xyz"}, "instance_follows": {"domain": "chitter.xyz"}, "instance_followers":
6 {"domain": "chitter.xyz"}, "keys": ["active_users", "new_users", "opened_reports",
7 "resolved_reports", "instance_accounts", "instance_media_attachments", "instance_reports",
8 "instance_statuses", "instance_follows", "instance_followers"], "start_at":
9 "2022-11-22T00:42:51+00:00", "end_at": "2022-11-27T00:42:51+00:00"}'
10 headers:
11 Accept:
12 - '*/*'
13 Accept-Encoding:
14 - gzip, deflate
15 Authorization:
16 - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2
17 Connection:
18 - keep-alive
19 Content-Length:
20 - '587'
21 Content-Type:
22 - application/json
23 User-Agent:
24 - tests/v311
25 method: POST
26 uri: http://localhost:3000/api/v1/admin/measures
27 response:
28 body:
29 string: '[{"key":"active_users","unit":null,"total":"2","previous_total":"0","data":[{"date":"2022-11-22T00:00:00Z","value":"0"},{"date":"2022-11-23T00:00:00Z","value":"0"},{"date":"2022-11-24T00:00:00Z","value":"0"},{"date":"2022-11-25T00:00:00Z","value":"0"},{"date":"2022-11-26T00:00:00Z","value":"2"}]},{"key":"new_users","unit":null,"total":"4","previous_total":"0","data":[{"date":"2022-11-22T00:00:00.000+00:00","value":"0"},{"date":"2022-11-23T00:00:00.000+00:00","value":"0"},{"date":"2022-11-24T00:00:00.000+00:00","value":"0"},{"date":"2022-11-25T00:00:00.000+00:00","value":"0"},{"date":"2022-11-26T00:00:00.000+00:00","value":"4"},{"date":"2022-11-27T00:00:00.000+00:00","value":"0"}]},{"key":"opened_reports","unit":null,"total":"0","previous_total":"0","data":[{"date":"2022-11-22T00:00:00.000+00:00","value":"0"},{"date":"2022-11-23T00:00:00.000+00:00","value":"0"},{"date":"2022-11-24T00:00:00.000+00:00","value":"0"},{"date":"2022-11-25T00:00:00.000+00:00","value":"0"},{"date":"2022-11-26T00:00:00.000+00:00","value":"0"},{"date":"2022-11-27T00:00:00.000+00:00","value":"0"}]},{"key":"resolved_reports","unit":null,"total":"0","previous_total":"0","data":[{"date":"2022-11-22T00:00:00.000+00:00","value":"0"},{"date":"2022-11-23T00:00:00.000+00:00","value":"0"},{"date":"2022-11-24T00:00:00.000+00:00","value":"0"},{"date":"2022-11-25T00:00:00.000+00:00","value":"0"},{"date":"2022-11-26T00:00:00.000+00:00","value":"0"},{"date":"2022-11-27T00:00:00.000+00:00","value":"0"}]},{"key":"instance_accounts","unit":null,"total":"0","data":[{"date":"2022-11-22T00:00:00.000+00:00","value":"0"},{"date":"2022-11-23T00:00:00.000+00:00","value":"0"},{"date":"2022-11-24T00:00:00.000+00:00","value":"0"},{"date":"2022-11-25T00:00:00.000+00:00","value":"0"},{"date":"2022-11-26T00:00:00.000+00:00","value":"0"},{"date":"2022-11-27T00:00:00.000+00:00","value":"0"}]},{"key":"instance_media_attachments","unit":"bytes","total":"0","human_value":"0
30 Bytes","data":[{"date":"2022-11-22T00:00:00.000+00:00","value":""},{"date":"2022-11-23T00:00:00.000+00:00","value":""},{"date":"2022-11-24T00:00:00.000+00:00","value":""},{"date":"2022-11-25T00:00:00.000+00:00","value":""},{"date":"2022-11-26T00:00:00.000+00:00","value":""},{"date":"2022-11-27T00:00:00.000+00:00","value":""}]},{"key":"instance_reports","unit":null,"total":"0","data":[{"date":"2022-11-22T00:00:00.000+00:00","value":"0"},{"date":"2022-11-23T00:00:00.000+00:00","value":"0"},{"date":"2022-11-24T00:00:00.000+00:00","value":"0"},{"date":"2022-11-25T00:00:00.000+00:00","value":"0"},{"date":"2022-11-26T00:00:00.000+00:00","value":"0"},{"date":"2022-11-27T00:00:00.000+00:00","value":"0"}]},{"key":"instance_statuses","unit":null,"total":"0","data":[{"date":"2022-11-22T00:00:00.000+00:00","value":"0"},{"date":"2022-11-23T00:00:00.000+00:00","value":"0"},{"date":"2022-11-24T00:00:00.000+00:00","value":"0"},{"date":"2022-11-25T00:00:00.000+00:00","value":"0"},{"date":"2022-11-26T00:00:00.000+00:00","value":"0"},{"date":"2022-11-27T00:00:00.000+00:00","value":"0"}]},{"key":"instance_follows","unit":null,"total":"0","data":[{"date":"2022-11-22T00:00:00.000+00:00","value":"0"},{"date":"2022-11-23T00:00:00.000+00:00","value":"0"},{"date":"2022-11-24T00:00:00.000+00:00","value":"0"},{"date":"2022-11-25T00:00:00.000+00:00","value":"0"},{"date":"2022-11-26T00:00:00.000+00:00","value":"0"},{"date":"2022-11-27T00:00:00.000+00:00","value":"0"}]},{"key":"instance_followers","unit":null,"total":"0","data":[{"date":"2022-11-22T00:00:00.000+00:00","value":"0"},{"date":"2022-11-23T00:00:00.000+00:00","value":"0"},{"date":"2022-11-24T00:00:00.000+00:00","value":"0"},{"date":"2022-11-25T00:00:00.000+00:00","value":"0"},{"date":"2022-11-26T00:00:00.000+00:00","value":"0"},{"date":"2022-11-27T00:00:00.000+00:00","value":"0"}]}]'
31 headers:
32 Cache-Control:
33 - no-store
34 Content-Security-Policy:
35 - 'base-uri ''none''; default-src ''none''; frame-ancestors ''none''; font-src
36 ''self'' http://localhost:3000; img-src ''self'' https: data: blob: http://localhost:3000;
37 style-src ''self'' http://localhost:3000 ''nonce-drS6KPeE5pwtqRFGPVh3ww=='';
38 media-src ''self'' https: data: http://localhost:3000; frame-src ''self''
39 https:; manifest-src ''self'' http://localhost:3000; connect-src ''self''
40 data: blob: http://localhost:3000 http://localhost:3000 ws://localhost:4000
41 ws://localhost:3035 http://localhost:3035; script-src ''self'' ''unsafe-inline''
42 ''unsafe-eval'' http://localhost:3000; child-src ''self'' blob: http://localhost:3000;
43 worker-src ''self'' blob: http://localhost:3000'
44 Content-Type:
45 - application/json; charset=utf-8
46 ETag:
47 - W/"bb40e02b66cfdf5be1ff5a980c8242af"
48 Referrer-Policy:
49 - strict-origin-when-cross-origin
50 Transfer-Encoding:
51 - chunked
52 Vary:
53 - Accept, Origin
54 X-Content-Type-Options:
55 - nosniff
56 X-Download-Options:
57 - noopen
58 X-Frame-Options:
59 - SAMEORIGIN
60 X-Permitted-Cross-Domain-Policies:
61 - none
62 X-Request-Id:
63 - 11b2cb9c-d3c4-41ba-802d-888e1ee62c9e
64 X-Runtime:
65 - '0.540102'
66 X-XSS-Protection:
67 - 1; mode=block
68 status:
69 code: 200
70 message: OK
71- request:
72 body: '{"instance_accounts": {"domain": "chitter.xyz"}, "instance_languages":
73 {"domain": "chitter.xyz"}, "keys": ["languages", "sources", "servers", "space_usage",
74 "instance_accounts", "instance_languages"], "limit": 3, "start_at": "2022-11-22T00:42:52+00:00",
75 "end_at": "2022-11-27T00:42:52+00:00"}'
76 headers:
77 Accept:
78 - '*/*'
79 Accept-Encoding:
80 - gzip, deflate
81 Authorization:
82 - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2
83 Connection:
84 - keep-alive
85 Content-Length:
86 - '292'
87 Content-Type:
88 - application/json
89 User-Agent:
90 - tests/v311
91 method: POST
92 uri: http://localhost:3000/api/v1/admin/dimensions
93 response:
94 body:
95 string: '[{"key":"languages","data":[{"key":"de","human_key":"German","value":"1"}]},{"key":"sources","data":[{"key":"web","human_key":"Website","value":"4"}]},{"key":"servers","data":[]},{"key":"space_usage","data":[{"key":"postgresql","human_key":"PostgreSQL","value":"16610095","unit":"bytes","human_value":"15.8
96 MB"},{"key":"redis","human_key":"Redis","value":"1560216","unit":"bytes","human_value":"1.49
97 MB"},{"key":"media","human_key":"Media storage","value":"0","unit":"bytes","human_value":"0
98 Bytes"}]},{"key":"instance_accounts","data":[]},{"key":"instance_languages","data":[]}]'
99 headers:
100 Cache-Control:
101 - no-store
102 Content-Security-Policy:
103 - 'base-uri ''none''; default-src ''none''; frame-ancestors ''none''; font-src
104 ''self'' http://localhost:3000; img-src ''self'' https: data: blob: http://localhost:3000;
105 style-src ''self'' http://localhost:3000 ''nonce-tTPpzIcGAZb7y2EXyfEsFg=='';
106 media-src ''self'' https: data: http://localhost:3000; frame-src ''self''
107 https:; manifest-src ''self'' http://localhost:3000; connect-src ''self''
108 data: blob: http://localhost:3000 http://localhost:3000 ws://localhost:4000
109 ws://localhost:3035 http://localhost:3035; script-src ''self'' ''unsafe-inline''
110 ''unsafe-eval'' http://localhost:3000; child-src ''self'' blob: http://localhost:3000;
111 worker-src ''self'' blob: http://localhost:3000'
112 Content-Type:
113 - application/json; charset=utf-8
114 ETag:
115 - W/"b5c3e0d37fd2fdab9859f566f5b2fa2e"
116 Referrer-Policy:
117 - strict-origin-when-cross-origin
118 Transfer-Encoding:
119 - chunked
120 Vary:
121 - Accept, Origin
122 X-Content-Type-Options:
123 - nosniff
124 X-Download-Options:
125 - noopen
126 X-Frame-Options:
127 - SAMEORIGIN
128 X-Permitted-Cross-Domain-Policies:
129 - none
130 X-Request-Id:
131 - 7332d9ca-00cb-4eaf-9d95-ee3baf8414f9
132 X-Runtime:
133 - '0.066790'
134 X-XSS-Protection:
135 - 1; mode=block
136 status:
137 code: 200
138 message: OK
139- request:
140 body: start_at=2022-11-17T00%3A42%3A52%2B00%3A00&end_at=2022-11-27T00%3A42%3A52%2B00%3A00&frequency=day
141 headers:
142 Accept:
143 - '*/*'
144 Accept-Encoding:
145 - gzip, deflate
146 Authorization:
147 - Bearer __MASTODON_PY_TEST_ACCESS_TOKEN_2
148 Connection:
149 - keep-alive
150 Content-Length:
151 - '97'
152 Content-Type:
153 - application/x-www-form-urlencoded
154 User-Agent:
155 - tests/v311
156 method: POST
157 uri: http://localhost:3000/api/v1/admin/retention
158 response:
159 body:
160 string: '[{"period":"2022-11-17T00:00:00+00:00","frequency":"day","data":[{"date":"2022-11-17T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-18T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-19T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-20T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-21T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-22T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-23T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-24T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-25T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-26T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-27T00:00:00+00:00","rate":0.0,"value":"0"}]},{"period":"2022-11-18T00:00:00+00:00","frequency":"day","data":[{"date":"2022-11-18T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-19T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-20T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-21T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-22T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-23T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-24T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-25T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-26T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-27T00:00:00+00:00","rate":0.0,"value":"0"}]},{"period":"2022-11-19T00:00:00+00:00","frequency":"day","data":[{"date":"2022-11-19T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-20T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-21T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-22T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-23T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-24T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-25T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-26T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-27T00:00:00+00:00","rate":0.0,"value":"0"}]},{"period":"2022-11-20T00:00:00+00:00","frequency":"day","data":[{"date":"2022-11-20T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-21T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-22T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-23T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-24T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-25T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-26T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-27T00:00:00+00:00","rate":0.0,"value":"0"}]},{"period":"2022-11-21T00:00:00+00:00","frequency":"day","data":[{"date":"2022-11-21T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-22T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-23T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-24T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-25T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-26T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-27T00:00:00+00:00","rate":0.0,"value":"0"}]},{"period":"2022-11-22T00:00:00+00:00","frequency":"day","data":[{"date":"2022-11-22T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-23T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-24T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-25T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-26T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-27T00:00:00+00:00","rate":0.0,"value":"0"}]},{"period":"2022-11-23T00:00:00+00:00","frequency":"day","data":[{"date":"2022-11-23T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-24T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-25T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-26T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-27T00:00:00+00:00","rate":0.0,"value":"0"}]},{"period":"2022-11-24T00:00:00+00:00","frequency":"day","data":[{"date":"2022-11-24T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-25T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-26T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-27T00:00:00+00:00","rate":0.0,"value":"0"}]},{"period":"2022-11-25T00:00:00+00:00","frequency":"day","data":[{"date":"2022-11-25T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-26T00:00:00+00:00","rate":0.0,"value":"0"},{"date":"2022-11-27T00:00:00+00:00","rate":0.0,"value":"0"}]},{"period":"2022-11-26T00:00:00+00:00","frequency":"day","data":[{"date":"2022-11-26T00:00:00+00:00","rate":0.5,"value":"2"},{"date":"2022-11-27T00:00:00+00:00","rate":0.0,"value":"0"}]},{"period":"2022-11-27T00:00:00+00:00","frequency":"day","data":[{"date":"2022-11-27T00:00:00+00:00","rate":0.0,"value":"0"}]}]'
161 headers:
162 Cache-Control:
163 - no-store
164 Content-Security-Policy:
165 - 'base-uri ''none''; default-src ''none''; frame-ancestors ''none''; font-src
166 ''self'' http://localhost:3000; img-src ''self'' https: data: blob: http://localhost:3000;
167 style-src ''self'' http://localhost:3000 ''nonce-AkyM5KkEra/OBSMZSu3SqQ=='';
168 media-src ''self'' https: data: http://localhost:3000; frame-src ''self''
169 https:; manifest-src ''self'' http://localhost:3000; connect-src ''self''
170 data: blob: http://localhost:3000 http://localhost:3000 ws://localhost:4000
171 ws://localhost:3035 http://localhost:3035; script-src ''self'' ''unsafe-inline''
172 ''unsafe-eval'' http://localhost:3000; child-src ''self'' blob: http://localhost:3000;
173 worker-src ''self'' blob: http://localhost:3000'
174 Content-Type:
175 - application/json; charset=utf-8
176 ETag:
177 - W/"c607f49eb27c19d561dab0434594a06e"
178 Referrer-Policy:
179 - strict-origin-when-cross-origin
180 Transfer-Encoding:
181 - chunked
182 Vary:
183 - Accept, Origin
184 X-Content-Type-Options:
185 - nosniff
186 X-Download-Options:
187 - noopen
188 X-Frame-Options:
189 - SAMEORIGIN
190 X-Permitted-Cross-Domain-Policies:
191 - none
192 X-Request-Id:
193 - 67a94c6b-e1fe-4c60-90c5-478c98e3e0f7
194 X-Runtime:
195 - '0.435749'
196 X-XSS-Protection:
197 - 1; mode=block
198 status:
199 code: 200
200 message: OK
201version: 1
diff --git a/tests/setup.sql b/tests/setup.sql
index c9e908d..1d19bc8 100644
--- a/tests/setup.sql
+++ b/tests/setup.sql
@@ -27,6 +27,11 @@ UPDATE users SET
27 encrypted_password = '$2a$10$8eAdhF69RiZiV0puZ.8iOOgMqBACmwJu8Z9X4CiN91iwRXbeC2jvi' 27 encrypted_password = '$2a$10$8eAdhF69RiZiV0puZ.8iOOgMqBACmwJu8Z9X4CiN91iwRXbeC2jvi'
28WHERE email = 'mastodonpy_test_2@localhost:3000'; 28WHERE email = 'mastodonpy_test_2@localhost:3000';
29 29
30UPDATE users SET
31 locale = 'de',
32 encrypted_password = '$2a$10$8eAdhF69RiZiV0puZ.8iOOgMqBACmwJu8Z9X4CiN91iwRXbeC2jvi'
33WHERE email = '[email protected]';
34
30INSERT INTO oauth_applications ( 35INSERT INTO oauth_applications (
31 id, 36 id,
32 name, 37 name,
diff --git a/tests/test_admin.py b/tests/test_admin.py
index 887ed14..49d9876 100644
--- a/tests/test_admin.py
+++ b/tests/test_admin.py
@@ -1,5 +1,7 @@
1import pytest 1import pytest
2import time 2import time
3from datetime import datetime, timedelta
4from mastodon import MastodonIllegalArgumentError
3 5
4@pytest.mark.vcr() 6@pytest.mark.vcr()
5def test_admin_accounts(api2): 7def test_admin_accounts(api2):
@@ -134,3 +136,49 @@ def test_admin_domain_blocks(api2):
134 assert block3.private_comment == "jk ilu <3" 136 assert block3.private_comment == "jk ilu <3"
135 api2.admin_delete_domain_block(block2) 137 api2.admin_delete_domain_block(block2)
136 assert not block3.id in map(lambda x: x.id, api2.admin_domain_blocks()) 138 assert not block3.id in map(lambda x: x.id, api2.admin_domain_blocks())
139
140@pytest.mark.vcr()
141def test_admin_stats(api2):
142 assert api2.admin_measures(
143 datetime.now() - timedelta(hours=24*5),
144 datetime.now(),
145 active_users=True,
146 new_users=True,
147 opened_reports=True,
148 resolved_reports=True,
149 instance_accounts="chitter.xyz",
150 instance_media_attachments="chitter.xyz",
151 instance_reports="http://chitter.xyz/",
152 instance_statuses="chitter.xyz",
153 instance_follows="http://chitter.xyz",
154 instance_followers="chitter.xyz",
155 #tag_accounts=0,
156 #tag_uses=0,
157 #tag_servers=0,
158 )
159
160 assert api2.admin_dimensions(
161 datetime.now() - timedelta(hours=24*5),
162 datetime.now(),
163 limit=3,
164 languages=True,
165 sources=True,
166 servers=True,
167 space_usage=True,
168 #tag_servers=0,
169 #tag_languages=0,
170 instance_accounts="chitter.xyz",
171 instance_languages="https://chitter.xyz"
172 )
173
174 api2.admin_retention(
175 datetime.now() - timedelta(days=10),
176 datetime.now()
177 )
178
179 with pytest.raises(MastodonIllegalArgumentError):
180 api2.admin_retention(
181 datetime.now() - timedelta(days=10),
182 datetime.now(),
183 frequency="dayz"
184 ) \ No newline at end of file
Powered by cgit v1.2.3 (git 2.41.0)