aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO.md4
-rw-r--r--docs/index.rst49
-rw-r--r--mastodon/Mastodon.py139
-rw-r--r--tests/cassettes/test_admin_stats.yaml201
-rw-r--r--tests/setup.sql5
-rw-r--r--tests/test_admin.py48
6 files changed, 436 insertions, 10 deletions
diff --git a/TODO.md b/TODO.md
index 8e5dad4..b61cee2 100644
--- a/TODO.md
+++ b/TODO.md
@@ -44,7 +44,7 @@ Refer to mastodon changelog and API docs for details when implementing, add or m
44* [x] Add support for incoming edited posts 44* [x] Add support for incoming edited posts
45* [x] Add notifications for posts deleted by moderators <- by email. not actually API relevant. 45* [x] Add notifications for posts deleted by moderators <- by email. not actually API relevant.
46* [x] Add explore page with trending posts and links 46* [x] Add explore page with trending posts and links
47* [ ] Add graphs and retention metrics to admin dashboard 47* [x] Add graphs and retention metrics to admin dashboard
48* [ ] Add GET /api/v1/accounts/familiar_followers to REST API 48* [ ] Add GET /api/v1/accounts/familiar_followers to REST API
49* [ ] Add POST /api/v1/accounts/:id/remove_from_followers to REST API 49* [ ] Add POST /api/v1/accounts/:id/remove_from_followers to REST API
50* [x] Add category and rule_ids params to POST /api/v1/reports IN REST API 50* [x] Add category and rule_ids params to POST /api/v1/reports IN REST API
@@ -55,7 +55,7 @@ Refer to mastodon changelog and API docs for details when implementing, add or m
55 55
563.5.3 563.5.3
57----- 57-----
58* [ ] Add limited attribute to accounts in REST API 58* [later with tool to update dicts] Add limited attribute to accounts in REST API
59 59
604.0.0 and beyond 604.0.0 and beyond
61---------------- 61----------------
diff --git a/docs/index.rst b/docs/index.rst
index 95ae0aa..e7d52dc 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -345,6 +345,18 @@ Toot dicts
345 'poll': # A poll dict if a poll is attached to this status. 345 'poll': # A poll dict if a poll is attached to this status.
346 } 346 }
347 347
348Status edit dicts
349~~~~~~~~~~~~~~~~~
350.. _status edit dict:
351
352.. code-block:: python
353
354 mastodonstatus_history(id)[0]
355 # Returns the following dictionary
356 {
357 TODO
358 }
359
348Mention dicts 360Mention dicts
349~~~~~~~~~~~~~ 361~~~~~~~~~~~~~
350.. _mention dict: 362.. _mention dict:
@@ -902,13 +914,37 @@ Admin domain block dicts
902 'obfuscate': #Boolean. True if domain name is obfuscated when listing. 914 'obfuscate': #Boolean. True if domain name is obfuscated when listing.
903 } 915 }
904 916
905Status edit dicts 917Admin measure dicts
906~~~~~~~~~~~~~~~~~ 918~~~~~~~~~~~~~~~~~~~
907.. _status edit dict: 919.. _admin measure dict:
908 920
909.. code-block:: python 921.. code-block:: python
910 922
911 mastodonstatus_history(id)[0] 923 api.admin_measures(datetime.now() - timedelta(hours=24*5), datetime.now(), active_users=True)
924 # Returns the following dictionary
925 {
926 TODO
927 }
928
929Admin dimension dicts
930~~~~~~~~~~~~~~~~~~~~~
931.. _admin dimension dict:
932
933.. code-block:: python
934
935 api.admin_dimensions(datetime.now() - timedelta(hours=24*5), datetime.now(), languages=True)
936 # Returns the following dictionary
937 {
938 TODO
939 }
940
941Admin retention dicts
942~~~~~~~~~~~~~~~~~~~~~
943.. _admin retention dict:
944
945.. code-block:: python
946
947 api.admin_retention(datetime.now() - timedelta(hours=24*5), datetime.now())
912 # Returns the following dictionary 948 # Returns the following dictionary
913 { 949 {
914 TODO 950 TODO
@@ -1471,6 +1507,11 @@ have admin: scopes attached with a lot of care, but be extra careful with those
1471.. automethod:: Mastodon.admin_update_domain_block 1507.. automethod:: Mastodon.admin_update_domain_block
1472.. automethod:: Mastodon.admin_delete_domain_block 1508.. automethod:: Mastodon.admin_delete_domain_block
1473 1509
1510.. automethod:: Mastodon.admin_measures
1511.. automethod:: Mastodon.admin_dimensions
1512.. automethod:: Mastodon.admin_retention
1513
1514
1474Acknowledgements 1515Acknowledgements
1475---------------- 1516----------------
1476Mastodon.py contains work by a large number of contributors, many of which have 1517Mastodon.py contains work by a large number of contributors, many of which have
diff --git a/mastodon/Mastodon.py b/mastodon/Mastodon.py
index 82e058c..03aa413 100644
--- a/mastodon/Mastodon.py
+++ b/mastodon/Mastodon.py
@@ -108,7 +108,6 @@ def api_version(created_ver, last_changed_ver, return_value_ver):
108 raise MastodonVersionError( 108 raise MastodonVersionError(
109 "Version check failed (Need version " + version + ")") 109 "Version check failed (Need version " + version + ")")
110 elif major == self.mastodon_major and minor > self.mastodon_minor: 110 elif major == self.mastodon_major and minor > self.mastodon_minor:
111 print(self.mastodon_minor)
112 raise MastodonVersionError( 111 raise MastodonVersionError(
113 "Version check failed (Need version " + version + ")") 112 "Version check failed (Need version " + version + ")")
114 elif major == self.mastodon_major and minor == self.mastodon_minor and patch > self.mastodon_patch: 113 elif major == self.mastodon_major and minor == self.mastodon_minor and patch > self.mastodon_patch:
@@ -264,6 +263,9 @@ class Mastodon:
264 __DICT_VERSION_ANNOUNCEMENT = bigger_version("3.1.0", __DICT_VERSION_REACTION) 263 __DICT_VERSION_ANNOUNCEMENT = bigger_version("3.1.0", __DICT_VERSION_REACTION)
265 __DICT_VERSION_STATUS_EDIT = "3.5.0" 264 __DICT_VERSION_STATUS_EDIT = "3.5.0"
266 __DICT_VERSION_ADMIN_DOMAIN_BLOCK = "4.0.0" 265 __DICT_VERSION_ADMIN_DOMAIN_BLOCK = "4.0.0"
266 __DICT_VERSION_ADMIN_MEASURE = "3.5.0"
267 __DICT_VERSION_ADMIN_DIMENSION = "3.5.0"
268 __DICT_VERSION_ADMIN_RETENTION = "3.5.0"
267 269
268 ### 270 ###
269 # Registering apps 271 # Registering apps
@@ -432,7 +434,6 @@ class Mastodon:
432 try_base_url = secret_file.readline().rstrip() 434 try_base_url = secret_file.readline().rstrip()
433 if try_base_url is not None and len(try_base_url) != 0: 435 if try_base_url is not None and len(try_base_url) != 0:
434 try_base_url = Mastodon.__protocolize(try_base_url) 436 try_base_url = Mastodon.__protocolize(try_base_url)
435 print(self.api_base_url, try_base_url)
436 if not (self.api_base_url is None or try_base_url == self.api_base_url): 437 if not (self.api_base_url is None or try_base_url == self.api_base_url):
437 raise MastodonIllegalArgumentError('Mismatch in base URLs between files and/or specified') 438 raise MastodonIllegalArgumentError('Mismatch in base URLs between files and/or specified')
438 self.api_base_url = try_base_url 439 self.api_base_url = try_base_url
@@ -544,7 +545,6 @@ class Mastodon:
544 We parse this from the hopefully present "Date" header, but make no effort to compensate for latency. 545 We parse this from the hopefully present "Date" header, but make no effort to compensate for latency.
545 """ 546 """
546 response = self.__api_request("HEAD", "/", return_response_object=True) 547 response = self.__api_request("HEAD", "/", return_response_object=True)
547 print(response.headers)
548 if 'Date' in response.headers: 548 if 'Date' in response.headers:
549 server_time_datetime = dateutil.parser.parse(response.headers['Date']) 549 server_time_datetime = dateutil.parser.parse(response.headers['Date'])
550 550
@@ -3456,6 +3456,130 @@ class Mastodon:
3456 else: 3456 else:
3457 raise AttributeError("You must provide an id of an existing domain block to remove it.") 3457 raise AttributeError("You must provide an id of an existing domain block to remove it.")
3458 3458
3459 @api_version("3.5.0", "3.5.0", __DICT_VERSION_ADMIN_MEASURE)
3460 def admin_measures(self, start_at, end_at, active_users=False, new_users=False, interactions=False, opened_reports = False, resolved_reports=False,
3461 tag_accounts=None, tag_uses=None, tag_servers=None, instance_accounts=None, instance_media_attachments=None, instance_reports=None,
3462 instance_statuses=None, instance_follows=None, instance_followers=None):
3463 """
3464 Retrieves numerical instance information for the time period (at day granularity) between `start_at` and `end_at`.
3465
3466 * `active_users`: Pass true to retrieve the number of active users on your instance within the time period
3467 * `new_users`: Pass true to retrieve the number of users who joined your instance within the time period
3468 * `interactions`: Pass true to retrieve the number of interactions (favourites, boosts, replies) on local statuses within the time period
3469 * `opened_reports`: Pass true to retrieve the number of reports filed within the time period
3470 * `resolved_reports` = Pass true to retrieve the number of reports resolved within the time period
3471 * `tag_accounts`: Pass a tag ID to get the number of accounts which used that tag in at least one status within the time period
3472 * `tag_uses`: Pass a tag ID to get the number of statuses which used that tag within the time period
3473 * `tag_servers`: Pass a tag ID to to get the number of remote origin servers for statuses which used that tag within the time period
3474 * `instance_accounts`: Pass a domain to get the number of accounts originating from that remote domain within the time period
3475 * `instance_media_attachments`: Pass a domain to get the amount of space used by media attachments from that remote domain within the time period
3476 * `instance_reports`: Pass a domain to get the number of reports filed against accounts from that remote domain within the time period
3477 * `instance_statuses`: Pass a domain to get the number of statuses originating from that remote domain within the time period
3478 * `instance_follows`: Pass a domain to get the number of accounts from a remote domain followed by that local user within the time period
3479 * `instance_followers`: Pass a domain to get the number of local accounts followed by accounts from that remote domain within the time period
3480
3481 This API call is relatively expensive - watch your servers load if you want to get a lot of statistical data. Especially the instance_statuses stats
3482 might take a long time to compute and, in fact, time out.
3483
3484 There is currently no way to get tag IDs implemented in Mastodon.py, because the Mastodon public API does not implement one. This will be fixed in a future
3485 release.
3486
3487 Returns a list of `admin measure dicts`_.
3488 """
3489 params_init = locals()
3490 keys = []
3491 for key in ["active_users", "new_users", "interactions", "opened_reports", "resolved_reports"]:
3492 if params_init[key] == True:
3493 keys.append(key)
3494
3495 params = {}
3496 for key in ["tag_accounts", "tag_uses", "tag_servers"]:
3497 if params_init[key] is not None:
3498 keys.append(key)
3499 params[key] = {"id": self.__unpack_id(params_init[key])}
3500 for key in ["instance_accounts", "instance_media_attachments", "instance_reports", "instance_statuses", "instance_follows", "instance_followers"]:
3501 if params_init[key] is not None:
3502 keys.append(key)
3503 params[key] = {"domain": Mastodon.__deprotocolize(params_init[key]).split("/")[0]}
3504
3505 if len(keys) == 0:
3506 raise MastodonIllegalArgumentError("Must request at least one metric.")
3507
3508 params["keys"] = keys
3509 params["start_at"] = self.__consistent_isoformat_utc(start_at)
3510 params["end_at"] = self.__consistent_isoformat_utc(end_at)
3511
3512 return self.__api_request('POST', '/api/v1/admin/measures', params, use_json=True)
3513
3514 @api_version("3.5.0", "3.5.0", __DICT_VERSION_ADMIN_DIMENSION)
3515 def admin_dimensions(self, start_at, end_at, limit=None, languages=False, sources=False, servers=False, space_usage=False, software_versions=False,
3516 tag_servers=None, tag_languages=None, instance_accounts=None, instance_languages=None):
3517 """
3518 Retrieves primarily categorical instance information for the time period (at day granularity) between `start_at` and `end_at`.
3519
3520 * `languages`: Pass true to get the most-used languages on this server
3521 * `sources`: Pass true to get the most-used client apps on this server
3522 * `servers`: Pass true to get the remote servers with the most statuses
3523 * `space_usage`: Pass true to get the how much space is used by different components your software stack
3524 * `software_versions`: Pass true to get the version numbers for your software stack
3525 * `tag_servers`: Pass a tag ID to get the most-common servers for statuses including a trending tag
3526 * `tag_languages`: Pass a tag ID to get the most-used languages for statuses including a trending tag
3527 * `instance_accounts`: Pass a domain to get the most-followed accounts from a remote server
3528 * `instance_languages`: Pass a domain to get the most-used languages from a remote server
3529
3530 Pass `limit` to set how many results you want on queries where that makes sense.
3531
3532 This API call is relatively expensive - watch your servers load if you want to get a lot of statistical data.
3533
3534 There is currently no way to get tag IDs implemented in Mastodon.py, because the Mastodon public API does not implement one. This will be fixed in a future
3535 release.
3536
3537 Returns a list of `admin dimensions dicts`_.
3538 """
3539 params_init = locals()
3540 keys = []
3541 for key in ["languages", "sources", "servers", "space_usage", "software_versions"]:
3542 if params_init[key] == True:
3543 keys.append(key)
3544
3545 params = {}
3546 for key in ["tag_servers", "tag_languages"]:
3547 if params_init[key] is not None:
3548 keys.append(key)
3549 params[key] = {"id": self.__unpack_id(params_init[key])}
3550 for key in ["instance_accounts", "instance_languages"]:
3551 if params_init[key] is not None:
3552 keys.append(key)
3553 params[key] = {"domain": Mastodon.__deprotocolize(params_init[key]).split("/")[0]}
3554
3555 if len(keys) == 0:
3556 raise MastodonIllegalArgumentError("Must request at least one dimension.")
3557
3558 params["keys"] = keys
3559 if limit is not None:
3560 params["limit"] = limit
3561 params["start_at"] = self.__consistent_isoformat_utc(start_at)
3562 params["end_at"] = self.__consistent_isoformat_utc(end_at)
3563
3564 return self.__api_request('POST', '/api/v1/admin/dimensions', params, use_json=True)
3565
3566 @api_version("3.5.0", "3.5.0", __DICT_VERSION_ADMIN_RETENTION)
3567 def admin_retention(self, start_at, end_at, frequency="day"):
3568 """
3569 Gets user retention statistics (at `frequency` - "day" or "month" - granularity) between `start_at` and `end_at`.
3570
3571 Returns a list of `admin retention dicts`_
3572 """
3573 if not frequency in ["day", "month"]:
3574 raise MastodonIllegalArgumentError("Frequency must be day or month")
3575
3576 params = {
3577 "start_at": self.__consistent_isoformat_utc(start_at),
3578 "end_at": self.__consistent_isoformat_utc(end_at),
3579 "frequency": frequency
3580 }
3581 return self.__api_request('POST', '/api/v1/admin/retention', params)
3582
3459 ### 3583 ###
3460 # Push subscription crypto utilities 3584 # Push subscription crypto utilities
3461 ### 3585 ###
@@ -3942,7 +4066,6 @@ class Mastodon:
3942 if not response_object.ok: 4066 if not response_object.ok:
3943 try: 4067 try:
3944 response = response_object.json(object_hook=self.__json_hooks) 4068 response = response_object.json(object_hook=self.__json_hooks)
3945 print(response)
3946 if isinstance(response, dict) and 'error' in response: 4069 if isinstance(response, dict) and 'error' in response:
3947 error_msg = response['error'] 4070 error_msg = response['error']
3948 elif isinstance(response, str): 4071 elif isinstance(response, str):
@@ -4348,6 +4471,14 @@ class Mastodon:
4348 base_url = base_url.rstrip("/") 4471 base_url = base_url.rstrip("/")
4349 return base_url 4472 return base_url
4350 4473
4474 @staticmethod
4475 def __deprotocolize(base_url):
4476 """Internal helper to strip http and https from a URL"""
4477 if base_url.startswith("http://"):
4478 base_url = base_url[7:]
4479 elif base_url.startswith("https://") or base_url.startswith("onion://"):
4480 base_url = base_url[8:]
4481 return base_url
4351 4482
4352## 4483##
4353# Exceptions 4484# Exceptions
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)