vk-scripts 1.0.4__tar.gz → 1.0.5__tar.gz

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 (54) hide show
  1. vk_scripts-1.0.5/.git-blame-ignore-revs +1 -0
  2. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/.github/workflows/ci.yml +18 -3
  3. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/LICENSE.txt +1 -1
  4. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/PKG-INFO +3 -3
  5. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/pyproject.toml +9 -3
  6. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/test/bin/main.sh +1 -1
  7. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/test/bin/mutuals.sh +1 -1
  8. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/test/bin/sessions.sh +1 -1
  9. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/test/bin/status.sh +1 -1
  10. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/test/bin/status_once.sh +1 -1
  11. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/test/lib/test.sh +1 -1
  12. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/api.py +23 -14
  13. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/error.py +1 -1
  14. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/last_seen.py +1 -1
  15. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/mutuals.py +22 -12
  16. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/platform.py +7 -11
  17. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/tracking/__init__.py +5 -2
  18. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/tracking/db/__init__.py +1 -1
  19. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/tracking/db/backend/__init__.py +6 -2
  20. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/tracking/db/backend/csv.py +1 -1
  21. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/tracking/db/backend/log.py +12 -6
  22. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/tracking/db/backend/null.py +1 -1
  23. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/tracking/db/format.py +1 -1
  24. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/tracking/db/meta.py +1 -1
  25. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/tracking/db/record.py +1 -1
  26. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/tracking/db/timestamp.py +1 -1
  27. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/tracking/sessions.py +78 -44
  28. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/tracking/status.py +55 -29
  29. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/user.py +2 -2
  30. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/utils/bar_chart.py +20 -17
  31. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/utils/io.py +3 -5
  32. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/version.py +3 -2
  33. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk_scripts.egg-info/PKG-INFO +3 -3
  34. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk_scripts.egg-info/SOURCES.txt +1 -0
  35. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/.gitattributes +0 -0
  36. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/.github/dependabot.yml +0 -0
  37. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/.gitignore +0 -0
  38. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/.pylintrc +0 -0
  39. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/README.md +0 -0
  40. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/docs/images/date.png +0 -0
  41. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/docs/images/hour.png +0 -0
  42. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/docs/images/user.png +0 -0
  43. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/docs/images/weekday.png +0 -0
  44. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/docs/mutuals.md +0 -0
  45. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/docs/sessions.md +0 -0
  46. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/docs/status.md +0 -0
  47. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/setup.cfg +0 -0
  48. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/test/share/test_db.csv +0 -0
  49. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/__init__.py +0 -0
  50. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk/utils/__init__.py +0 -0
  51. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk_scripts.egg-info/dependency_links.txt +0 -0
  52. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk_scripts.egg-info/entry_points.txt +0 -0
  53. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk_scripts.egg-info/requires.txt +0 -0
  54. {vk_scripts-1.0.4 → vk_scripts-1.0.5}/vk_scripts.egg-info/top_level.txt +0 -0
@@ -0,0 +1 @@
1
+ 93ad1eb3a63041a49882e6b7b6b0ef506d4fa6c7
@@ -6,8 +6,8 @@ on:
6
6
  - 'docs/**'
7
7
  - 'README.md'
8
8
  schedule:
9
- # Weekly, at 6:45 AM on Thursday (somewhat randomly chosen).
10
- - cron: '45 6 * * 4'
9
+ # Weekly, at 02:30 on Friday (somewhat randomly chosen).
10
+ - cron: '30 02 * * 5'
11
11
  workflow_dispatch:
12
12
 
13
13
  env:
@@ -15,6 +15,21 @@ env:
15
15
  PIP_NO_PYTHON_VERSION_WARNING: 1
16
16
 
17
17
  jobs:
18
+ lint:
19
+ runs-on: ubuntu-latest
20
+ name: Linting
21
+ steps:
22
+ - name: Checkout
23
+ uses: actions/checkout@v6
24
+ with:
25
+ fetch-depth: 0
26
+ - name: Set up Python
27
+ uses: actions/setup-python@v6
28
+ with:
29
+ python-version: '3.x'
30
+ - name: Run black
31
+ uses: psf/black@stable
32
+
18
33
  test:
19
34
  strategy:
20
35
  matrix:
@@ -43,7 +58,7 @@ jobs:
43
58
  run: ./test/bin/main.sh
44
59
 
45
60
  publish_pypi:
46
- needs: [test]
61
+ needs: [lint, test]
47
62
  runs-on: ubuntu-latest
48
63
  name: Publish
49
64
  steps:
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2015 Egor Tensin <Egor.Tensin@gmail.com>
3
+ Copyright (c) 2015 Egor Tensin <egor@tensin.name>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vk_scripts
3
- Version: 1.0.4
3
+ Version: 1.0.5
4
4
  Summary: Scripts to stalk people on VK
5
- Author-email: Egor Tensin <Egor.Tensin@gmail.com>
5
+ Author-email: Egor Tensin <egor@tensin.name>
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/egor-tensin/vk-scripts
8
8
  Project-URL: Bug Tracker, https://github.com/egor-tensin/vk-scripts/issues
9
- Classifier: Development Status :: 4 - Beta
9
+ Classifier: Development Status :: 5 - Production/Stable
10
10
  Requires-Python: >=3.4
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE.txt
@@ -1,13 +1,16 @@
1
1
  [build-system]
2
- requires = ["setuptools>=61.0", "setuptools-scm"]
2
+ # The SPDX license identifiers are available from 77.0.3:
3
+ # https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license-and-license-files
4
+ requires = ["setuptools >= 77.0.3", "setuptools-scm"]
3
5
  build-backend = "setuptools.build_meta"
4
6
 
5
7
  [project]
6
8
  name = "vk_scripts"
7
9
  description = "Scripts to stalk people on VK"
8
10
  license = "MIT"
11
+ license-files = ["LICENSE.txt"]
9
12
  dynamic = ["version"]
10
- authors = [{name = "Egor Tensin", email = "Egor.Tensin@gmail.com"}]
13
+ authors = [{name = "Egor Tensin", email = "egor@tensin.name"}]
11
14
  readme = "README.md"
12
15
  requires-python = ">=3.4"
13
16
 
@@ -17,7 +20,7 @@ dependencies = [
17
20
  ]
18
21
 
19
22
  classifiers = [
20
- "Development Status :: 4 - Beta",
23
+ "Development Status :: 5 - Production/Stable",
21
24
  ]
22
25
 
23
26
  [project.urls]
@@ -30,3 +33,6 @@ vk-status = "vk.tracking.status:main"
30
33
  vk-mutuals = "vk.mutuals:main"
31
34
 
32
35
  [tool.setuptools_scm]
36
+
37
+ [tool.black]
38
+ skip-string-normalization = true
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
- # Copyright (c) 2019 Egor Tensin <Egor.Tensin@gmail.com>
3
+ # Copyright (c) 2019 Egor Tensin <egor@tensin.name>
4
4
  # This file is part of the "VK scripts" project.
5
5
  # For details, see https://github.com/egor-tensin/vk-scripts.
6
6
  # Distributed under the MIT License.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
- # Copyright (c) 2019 Egor Tensin <Egor.Tensin@gmail.com>
3
+ # Copyright (c) 2019 Egor Tensin <egor@tensin.name>
4
4
  # This file is part of the "VK scripts" project.
5
5
  # For details, see https://github.com/egor-tensin/vk-scripts.
6
6
  # Distributed under the MIT License.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
- # Copyright (c) 2019 Egor Tensin <Egor.Tensin@gmail.com>
3
+ # Copyright (c) 2019 Egor Tensin <egor@tensin.name>
4
4
  # This file is part of the "VK scripts" project.
5
5
  # For details, see https://github.com/egor-tensin/vk-scripts.
6
6
  # Distributed under the MIT License.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
- # Copyright (c) 2019 Egor Tensin <Egor.Tensin@gmail.com>
3
+ # Copyright (c) 2019 Egor Tensin <egor@tensin.name>
4
4
  # This file is part of the "VK scripts" project.
5
5
  # For details, see https://github.com/egor-tensin/vk-scripts.
6
6
  # Distributed under the MIT License.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
- # Copyright (c) 2019 Egor Tensin <Egor.Tensin@gmail.com>
3
+ # Copyright (c) 2019 Egor Tensin <egor@tensin.name>
4
4
  # This file is part of the "VK scripts" project.
5
5
  # For details, see https://github.com/egor-tensin/vk-scripts.
6
6
  # Distributed under the MIT License.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
- # Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
3
+ # Copyright (c) 2020 Egor Tensin <egor@tensin.name>
4
4
  # This file is part of the "VK scripts" project.
5
5
  # For details, see https://github.com/egor-tensin/vk-scripts.
6
6
  # Distributed under the MIT License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2015 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2015 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -26,9 +26,15 @@ def _filter_empty_params(params, empty_params=False):
26
26
  if empty_params:
27
27
  return params
28
28
  if isinstance(params, Mapping):
29
- return {name: value for name, value in params.items() if not _is_empty_param_value(value)}
29
+ return {
30
+ name: value
31
+ for name, value in params.items()
32
+ if not _is_empty_param_value(value)
33
+ }
30
34
  if isinstance(params, Iterable):
31
- return [(name, value) for name, value in params if not _is_empty_param_value(value)]
35
+ return [
36
+ (name, value) for name, value in params if not _is_empty_param_value(value)
37
+ ]
32
38
  raise TypeError()
33
39
 
34
40
 
@@ -64,6 +70,7 @@ ACCESS_TOKEN = '9722cef09722cef09722cef071974b8cbe997229722cef0cbabfd816916af6c7
64
70
 
65
71
 
66
72
  class Version(Enum):
73
+ # https://dev.vk.com/en/reference/versions
67
74
  V5_199 = '5.199'
68
75
  DEFAULT = V5_199
69
76
 
@@ -115,11 +122,11 @@ class API:
115
122
 
116
123
  def _call_method(self, method, **params):
117
124
  url = self._build_method_url(method, **params)
118
- #print(url)
125
+ # print(url)
119
126
  try:
120
127
  with urlopen(url) as response:
121
128
  response = json.loads(response.read().decode())
122
- #print(response)
129
+ # print(response)
123
130
  if 'response' not in response:
124
131
  raise vk.error.InvalidAPIResponseError(response)
125
132
  return response['response']
@@ -134,17 +141,19 @@ class API:
134
141
  return [user for user in user_list if not user.is_deactivated()]
135
142
 
136
143
  def users_get(self, user_ids, fields=(), deactivated_users=True):
137
- return self._filter_response_with_users(self._call_method(
138
- Method.USERS_GET,
139
- user_ids=_join_param_values(user_ids),
140
- fields=_join_param_values(fields)), deactivated_users)
144
+ return self._filter_response_with_users(
145
+ self._call_method(
146
+ Method.USERS_GET,
147
+ user_ids=_join_param_values(user_ids),
148
+ fields=_join_param_values(fields),
149
+ ),
150
+ deactivated_users,
151
+ )
141
152
 
142
153
  def friends_get(self, user_id, fields=(), deactivated_users=True):
143
154
  response = self._call_method(
144
- Method.FRIENDS_GET,
145
- user_id=user_id,
146
- fields=_join_param_values(fields))
155
+ Method.FRIENDS_GET, user_id=user_id, fields=_join_param_values(fields)
156
+ )
147
157
  if 'items' not in response:
148
158
  raise vk.error.InvalidAPIResponseError(response)
149
- return self._filter_response_with_users(response['items'],
150
- deactivated_users)
159
+ return self._filter_response_with_users(response['items'], deactivated_users)
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2016 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2016 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2015 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2015 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -14,7 +14,6 @@ from vk.user import UserField
14
14
  from vk.utils import io
15
15
  import vk.version
16
16
 
17
-
18
17
  _OUTPUT_USER_FIELDS = UserField.UID, UserField.FIRST_NAME, UserField.LAST_NAME
19
18
 
20
19
 
@@ -83,19 +82,30 @@ def _parse_args(args=None):
83
82
  args = sys.argv[1:]
84
83
 
85
84
  parser = argparse.ArgumentParser(
86
- description='Learn who your ex and her new boyfriend are both friends with.')
85
+ description='Learn who your ex and her new boyfriend are both friends with.'
86
+ )
87
87
 
88
88
  vk.version.add_to_arg_parser(parser)
89
89
 
90
- parser.add_argument('uids', metavar='UID', nargs='+',
91
- help='user IDs or "screen names"')
92
- parser.add_argument('-f', '--format', dest='out_fmt',
93
- type=_parse_output_format,
94
- default=OutputFormat.CSV,
95
- choices=OutputFormat,
96
- help='specify output format')
97
- parser.add_argument('-o', '--output', metavar='PATH', dest='out_path',
98
- help='set output file path (standard output by default)')
90
+ parser.add_argument(
91
+ 'uids', metavar='UID', nargs='+', help='user IDs or "screen names"'
92
+ )
93
+ parser.add_argument(
94
+ '-f',
95
+ '--format',
96
+ dest='out_fmt',
97
+ type=_parse_output_format,
98
+ default=OutputFormat.CSV,
99
+ choices=OutputFormat,
100
+ help='specify output format',
101
+ )
102
+ parser.add_argument(
103
+ '-o',
104
+ '--output',
105
+ metavar='PATH',
106
+ dest='out_path',
107
+ help='set output file path (standard output by default)',
108
+ )
99
109
 
100
110
  return parser.parse_args(args)
101
111
 
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2016 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -8,14 +8,14 @@ import re
8
8
 
9
9
 
10
10
  class Platform(Enum):
11
+ # https://dev.vk.com/en/reference/objects/user#last_seen
11
12
  MOBILE = 1
12
13
  IPHONE = 2
13
14
  IPAD = 3
14
15
  ANDROID = 4
15
16
  WINDOWS_PHONE = 5
16
- WINDOWS8 = 6
17
+ WINDOWS10 = 6
17
18
  WEB = 7
18
- VK_MOBILE = 8
19
19
 
20
20
  @staticmethod
21
21
  def from_string(s):
@@ -29,16 +29,13 @@ class Platform(Enum):
29
29
  m = re.search(r'\w', s)
30
30
  if m is None:
31
31
  return s
32
- return s[:m.start()] + m.group().upper() + s[m.end():]
32
+ return s[: m.start()] + m.group().upper() + s[m.end() :]
33
33
 
34
34
  def get_descr_header(self):
35
35
  return self._capitalize_first_letter(_PLATFORM_DESCRIPTIONS[self])
36
36
 
37
37
  def get_descr_text(self):
38
38
  s = _PLATFORM_DESCRIPTIONS[self]
39
- if self == Platform.VK_MOBILE:
40
- return s
41
- s = s.replace('unrecognized', 'an unrecognized')
42
39
  return 'the ' + s
43
40
 
44
41
  def get_descr_text_capitalized(self):
@@ -46,12 +43,11 @@ class Platform(Enum):
46
43
 
47
44
 
48
45
  _PLATFORM_DESCRIPTIONS = {
49
- Platform.MOBILE: '"mobile" web version (or unrecognized mobile app)',
46
+ Platform.MOBILE: '"mobile" web version (or an unrecognized mobile app)',
50
47
  Platform.IPHONE: 'official iPhone app',
51
48
  Platform.IPAD: 'official iPad app',
52
49
  Platform.ANDROID: 'official Android app',
53
50
  Platform.WINDOWS_PHONE: 'official Windows Phone app',
54
- Platform.WINDOWS8: 'official Windows 8 app',
55
- Platform.WEB: 'web version (or unrecognized app)',
56
- Platform.VK_MOBILE: 'VK Mobile',
51
+ Platform.WINDOWS10: 'official Windows 10 app',
52
+ Platform.WEB: 'web version (or an unrecognized app)',
57
53
  }
@@ -1,5 +1,8 @@
1
- # Copyright 2016 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright 2016 Egor Tensin <egor@tensin.name>
2
2
  # This file is licensed under the terms of the MIT License.
3
3
  # See LICENSE.txt for details.
4
4
 
5
- __all__ = 'sessions', 'status',
5
+ __all__ = (
6
+ 'sessions',
7
+ 'status',
8
+ )
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2016 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -1,8 +1,12 @@
1
- # Copyright (c) 2016 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2016 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
5
5
 
6
6
  from . import csv, log, null
7
7
 
8
- __all__ = 'csv', 'log', 'null',
8
+ __all__ = (
9
+ 'csv',
10
+ 'log',
11
+ 'null',
12
+ )
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2016 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2016 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -13,9 +13,11 @@ class Writer(meta.Writer):
13
13
  self._logger = logging.getLogger(__file__)
14
14
  self._logger.setLevel(logging.INFO)
15
15
  handler = logging.StreamHandler(fd)
16
- handler.setFormatter(logging.Formatter(
17
- fmt='[%(asctime)s] %(message)s',
18
- datefmt='%Y-%m-%d %H:%M:%S%z'))
16
+ handler.setFormatter(
17
+ logging.Formatter(
18
+ fmt='[%(asctime)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S%z'
19
+ )
20
+ )
19
21
  self._logger.addHandler(handler)
20
22
 
21
23
  self._reset_last_notification()
@@ -86,7 +88,8 @@ class Writer(meta.Writer):
86
88
  return '{} was last seen at {} using {}.'.format(
87
89
  Writer._format_user(user),
88
90
  user.get_last_seen_time_local(),
89
- user.get_last_seen_platform().get_descr_text())
91
+ user.get_last_seen_platform().get_descr_text(),
92
+ )
90
93
 
91
94
  @staticmethod
92
95
  def _format_user_went_online(user):
@@ -98,4 +101,7 @@ class Writer(meta.Writer):
98
101
 
99
102
  @staticmethod
100
103
  def _format_another_connection_error(e):
101
- return 'Encountered a connection error which looks like the previous one: ' + str(e)
104
+ return (
105
+ 'Encountered a connection error which looks like the previous one: '
106
+ + str(e)
107
+ )
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2016 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2016 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2017 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2016 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2016 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2016 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -339,10 +339,12 @@ class OutputSinkPlot(OutputSinkOnlineSessions):
339
339
 
340
340
  @staticmethod
341
341
  def _extract_values(durations):
342
- return (OutputSinkPlot._duration_to_seconds(duration) for duration in durations.values())
342
+ return (
343
+ OutputSinkPlot._duration_to_seconds(duration)
344
+ for duration in durations.values()
345
+ )
343
346
 
344
- def process_database(
345
- self, group_by, db_reader, time_from=None, time_to=None):
347
+ def process_database(self, group_by, db_reader, time_from=None, time_to=None):
346
348
 
347
349
  durations = group_by.group(db_reader, time_from, time_to)
348
350
 
@@ -350,8 +352,9 @@ class OutputSinkPlot(OutputSinkOnlineSessions):
350
352
  bar_chart.set_title(OutputSinkPlot.TITLE)
351
353
  bar_chart.enable_grid_for_values()
352
354
  bar_chart.only_integer_values()
353
- bar_chart.set_property(bar_chart.get_values_labels(),
354
- fontsize='small', rotation=30)
355
+ bar_chart.set_property(
356
+ bar_chart.get_values_labels(), fontsize='small', rotation=30
357
+ )
355
358
  bar_chart.set_value_label_formatter(self._format_duration)
356
359
 
357
360
  labels = tuple(self._extract_labels(group_by, durations))
@@ -363,9 +366,8 @@ class OutputSinkPlot(OutputSinkOnlineSessions):
363
366
  else:
364
367
  bar_height = bar_chart.THICK_BAR_HEIGHT
365
368
 
366
- bars = bar_chart.plot_bars(
367
- labels, durations, bar_height=bar_height)
368
- bar_chart.set_property(bars, alpha=.33)
369
+ bars = bar_chart.plot_bars(labels, durations, bar_height=bar_height)
370
+ bar_chart.set_property(bars, alpha=0.33)
369
371
 
370
372
  if self._fd is sys.stdout:
371
373
  bar_chart.show()
@@ -426,8 +428,7 @@ def _parse_date_range_limit(s):
426
428
  return dt.replace(tzinfo=timezone.utc)
427
429
  except ValueError:
428
430
  msg = 'invalid date range limit (must be in the \'{}\' format): {}'
429
- raise argparse.ArgumentTypeError(
430
- msg.format(_DATE_RANGE_LIMIT_FORMAT, s))
431
+ raise argparse.ArgumentTypeError(msg.format(_DATE_RANGE_LIMIT_FORMAT, s))
431
432
 
432
433
 
433
434
  def _parse_args(args=None):
@@ -435,44 +436,78 @@ def _parse_args(args=None):
435
436
  args = sys.argv[1:]
436
437
 
437
438
  parser = argparse.ArgumentParser(
438
- description='View/visualize the amount of time people spend online.')
439
+ description='View/visualize the amount of time people spend online.'
440
+ )
439
441
 
440
442
  vk.version.add_to_arg_parser(parser)
441
443
 
442
- parser.add_argument('db_path', metavar='input', nargs='?',
443
- help='database file path (standard input by default)')
444
- parser.add_argument('out_path', metavar='output', nargs='?',
445
- help='output file path (standard output by default)')
446
- parser.add_argument('-g', '--group-by',
447
- type=_parse_group_by,
448
- choices=GroupBy,
449
- default=GroupBy.USER,
450
- help='group online sessions by user/date/etc.')
451
- parser.add_argument('-i', '--input-format', dest='db_fmt',
452
- type=_parse_database_format,
453
- default=DatabaseFormat.CSV,
454
- choices=DatabaseFormat,
455
- help='specify database format')
456
- parser.add_argument('-o', '--output-format', dest='out_fmt',
457
- type=_parse_output_format,
458
- choices=OutputFormat,
459
- default=OutputFormat.CSV,
460
- help='specify output format')
461
- parser.add_argument('-a', '--from', dest='time_from',
462
- type=_parse_date_range_limit, default=None,
463
- help='discard online activity prior to this moment')
464
- parser.add_argument('-b', '--to', dest='time_to',
465
- type=_parse_date_range_limit, default=None,
466
- help='discard online activity after this moment')
444
+ parser.add_argument(
445
+ 'db_path',
446
+ metavar='input',
447
+ nargs='?',
448
+ help='database file path (standard input by default)',
449
+ )
450
+ parser.add_argument(
451
+ 'out_path',
452
+ metavar='output',
453
+ nargs='?',
454
+ help='output file path (standard output by default)',
455
+ )
456
+ parser.add_argument(
457
+ '-g',
458
+ '--group-by',
459
+ type=_parse_group_by,
460
+ choices=GroupBy,
461
+ default=GroupBy.USER,
462
+ help='group online sessions by user/date/etc.',
463
+ )
464
+ parser.add_argument(
465
+ '-i',
466
+ '--input-format',
467
+ dest='db_fmt',
468
+ type=_parse_database_format,
469
+ default=DatabaseFormat.CSV,
470
+ choices=DatabaseFormat,
471
+ help='specify database format',
472
+ )
473
+ parser.add_argument(
474
+ '-o',
475
+ '--output-format',
476
+ dest='out_fmt',
477
+ type=_parse_output_format,
478
+ choices=OutputFormat,
479
+ default=OutputFormat.CSV,
480
+ help='specify output format',
481
+ )
482
+ parser.add_argument(
483
+ '-a',
484
+ '--from',
485
+ dest='time_from',
486
+ type=_parse_date_range_limit,
487
+ default=None,
488
+ help='discard online activity prior to this moment',
489
+ )
490
+ parser.add_argument(
491
+ '-b',
492
+ '--to',
493
+ dest='time_to',
494
+ type=_parse_date_range_limit,
495
+ default=None,
496
+ help='discard online activity after this moment',
497
+ )
467
498
 
468
499
  return parser.parse_args(args)
469
500
 
470
501
 
471
502
  def process_online_sessions(
472
- db_path=None, db_fmt=DatabaseFormat.CSV,
473
- out_path=None, out_fmt=OutputFormat.CSV,
474
- group_by=GroupBy.USER,
475
- time_from=None, time_to=None):
503
+ db_path=None,
504
+ db_fmt=DatabaseFormat.CSV,
505
+ out_path=None,
506
+ out_fmt=OutputFormat.CSV,
507
+ group_by=GroupBy.USER,
508
+ time_from=None,
509
+ time_to=None,
510
+ ):
476
511
 
477
512
  if time_from is not None and time_to is not None:
478
513
  if time_from > time_to:
@@ -483,9 +518,8 @@ def process_online_sessions(
483
518
  with out_fmt.open_file(out_path) as out_fd:
484
519
  out_sink = out_fmt.create_sink(out_fd)
485
520
  out_sink.process_database(
486
- group_by, db_reader,
487
- time_from=time_from,
488
- time_to=time_to)
521
+ group_by, db_reader, time_from=time_from, time_to=time_to
522
+ )
489
523
 
490
524
 
491
525
  def main(args=None):
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2016 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -52,11 +52,16 @@ class StatusTracker:
52
52
  if not isinstance(fn, Callable):
53
53
  raise TypeError()
54
54
 
55
- _USER_FIELDS = UserField.DOMAIN, UserField.ONLINE, UserField.LAST_SEEN,
55
+ _USER_FIELDS = (
56
+ UserField.DOMAIN,
57
+ UserField.ONLINE,
58
+ UserField.LAST_SEEN,
59
+ )
56
60
 
57
61
  def _query_status(self, uids):
58
- user_list = self._api.users_get(uids, self._USER_FIELDS,
59
- deactivated_users=False)
62
+ user_list = self._api.users_get(
63
+ uids, self._USER_FIELDS, deactivated_users=False
64
+ )
60
65
  return {user.get_uid(): user for user in user_list}
61
66
 
62
67
  def _notify_status(self, user):
@@ -128,13 +133,11 @@ class StatusTracker:
128
133
  def _handle_sigint():
129
134
  # Python doesn't raise KeyboardInterrupt in case a real SIGINT is sent
130
135
  # from outside, surprisingly.
131
- return StatusTracker._handle_signal(signal.SIGINT,
132
- StatusTracker._stop_looping)
136
+ return StatusTracker._handle_signal(signal.SIGINT, StatusTracker._stop_looping)
133
137
 
134
138
  @staticmethod
135
139
  def _handle_sigterm():
136
- return StatusTracker._handle_signal(signal.SIGTERM,
137
- StatusTracker._stop_looping)
140
+ return StatusTracker._handle_signal(signal.SIGTERM, StatusTracker._stop_looping)
138
141
 
139
142
  def loop(self, uids):
140
143
  with self._handle_sigint(), self._handle_sigterm():
@@ -169,35 +172,58 @@ def _parse_args(args=None):
169
172
  if args is None:
170
173
  args = sys.argv[1:]
171
174
 
172
- parser = argparse.ArgumentParser(
173
- description='Track when people go online/offline.')
175
+ parser = argparse.ArgumentParser(description='Track when people go online/offline.')
174
176
 
175
177
  vk.version.add_to_arg_parser(parser)
176
178
 
177
- parser.add_argument('uids', metavar='UID', nargs='+',
178
- help='user IDs or "screen names"')
179
- parser.add_argument('-t', '--timeout', metavar='SECONDS',
180
- type=_parse_positive_integer,
181
- default=DEFAULT_TIMEOUT,
182
- help='set refresh interval')
183
- parser.add_argument('-O', '--only-once', action='store_true',
184
- help='query the status only once and exit')
185
- parser.add_argument('-l', '--log', metavar='PATH', dest='log_path',
186
- help='set log file path (standard output by default)')
187
- parser.add_argument('-f', '--format', dest='db_fmt',
188
- type=_parse_database_format,
189
- choices=DatabaseFormat,
190
- default=DEFAULT_DB_FORMAT,
191
- help='specify database format')
192
- parser.add_argument('-o', '--output', metavar='PATH', dest='db_path',
193
- help='set database file path')
179
+ parser.add_argument(
180
+ 'uids', metavar='UID', nargs='+', help='user IDs or "screen names"'
181
+ )
182
+ parser.add_argument(
183
+ '-t',
184
+ '--timeout',
185
+ metavar='SECONDS',
186
+ type=_parse_positive_integer,
187
+ default=DEFAULT_TIMEOUT,
188
+ help='set refresh interval',
189
+ )
190
+ parser.add_argument(
191
+ '-O',
192
+ '--only-once',
193
+ action='store_true',
194
+ help='query the status only once and exit',
195
+ )
196
+ parser.add_argument(
197
+ '-l',
198
+ '--log',
199
+ metavar='PATH',
200
+ dest='log_path',
201
+ help='set log file path (standard output by default)',
202
+ )
203
+ parser.add_argument(
204
+ '-f',
205
+ '--format',
206
+ dest='db_fmt',
207
+ type=_parse_database_format,
208
+ choices=DatabaseFormat,
209
+ default=DEFAULT_DB_FORMAT,
210
+ help='specify database format',
211
+ )
212
+ parser.add_argument(
213
+ '-o', '--output', metavar='PATH', dest='db_path', help='set database file path'
214
+ )
194
215
 
195
216
  return parser.parse_args(args)
196
217
 
197
218
 
198
219
  def track_status(
199
- uids, timeout=DEFAULT_TIMEOUT, log_path=None,
200
- db_path=None, db_fmt=DEFAULT_DB_FORMAT, only_once=False):
220
+ uids,
221
+ timeout=DEFAULT_TIMEOUT,
222
+ log_path=None,
223
+ db_path=None,
224
+ db_fmt=DEFAULT_DB_FORMAT,
225
+ only_once=False,
226
+ ):
201
227
 
202
228
  api = API()
203
229
  tracker = StatusTracker(api, timeout)
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2016 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -79,7 +79,7 @@ class User(Hashable, MutableMapping):
79
79
 
80
80
  def __eq__(self, other):
81
81
  return self.get_uid() == other.get_uid()
82
- #return self._fields == other._fields
82
+ # return self._fields == other._fields
83
83
 
84
84
  def __hash__(self):
85
85
  return hash(self.get_uid())
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2017 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -9,7 +9,7 @@ import numpy as np
9
9
 
10
10
 
11
11
  class BarChartBuilder:
12
- _BAR_HEIGHT = .5
12
+ _BAR_HEIGHT = 0.5
13
13
 
14
14
  THICK_BAR_HEIGHT = _BAR_HEIGHT
15
15
  THIN_BAR_HEIGHT = THICK_BAR_HEIGHT / 2
@@ -112,12 +112,11 @@ class BarChartBuilder:
112
112
 
113
113
  self._get_categories_axis().set_ticklabels(categories)
114
114
 
115
- bars = self._ax.barh(bar_offsets, values, align='center',
116
- height=bar_height)
115
+ bars = self._ax.barh(bar_offsets, values, align='center', height=bar_height)
117
116
 
118
117
  if min(values) >= 0:
119
118
  self.set_values_axis_limits(start=0)
120
- if np.isclose(max(values), 0.):
119
+ if np.isclose(max(values), 0.0):
121
120
  self.set_values_axis_limits(end=self._DEFAULT_VALUES_AXIS_MAX)
122
121
  elif max(values) < 0:
123
122
  self.set_values_axis_limits(end=0)
@@ -134,23 +133,27 @@ class BarChartBuilder:
134
133
 
135
134
  if __name__ == '__main__':
136
135
  import argparse
136
+
137
137
  parser = argparse.ArgumentParser()
138
138
 
139
- parser.add_argument('--categories', nargs='*', metavar='LABEL',
140
- default=[])
141
- parser.add_argument('--values', nargs='*', metavar='N',
142
- default=[], type=float)
139
+ parser.add_argument('--categories', nargs='*', metavar='LABEL', default=[])
140
+ parser.add_argument('--values', nargs='*', metavar='N', default=[], type=float)
143
141
 
144
142
  parser.add_argument('--output', '-o', help='set output file path')
145
143
 
146
- parser.add_argument('--align-middle', action='store_true',
147
- dest='labels_align_middle',
148
- help='align labels to the middle of the bars')
144
+ parser.add_argument(
145
+ '--align-middle',
146
+ action='store_true',
147
+ dest='labels_align_middle',
148
+ help='align labels to the middle of the bars',
149
+ )
149
150
 
150
- parser.add_argument('--integer-values', action='store_true',
151
- dest='only_integer_values')
152
- parser.add_argument('--any-values', action='store_false',
153
- dest='only_integer_values')
151
+ parser.add_argument(
152
+ '--integer-values', action='store_true', dest='only_integer_values'
153
+ )
154
+ parser.add_argument(
155
+ '--any-values', action='store_false', dest='only_integer_values'
156
+ )
154
157
 
155
158
  parser.add_argument('--grid-categories', action='store_true')
156
159
  parser.add_argument('--grid-values', action='store_true')
@@ -160,7 +163,7 @@ if __name__ == '__main__':
160
163
  if len(args.categories) < len(args.values):
161
164
  parser.error('too many bar values')
162
165
  if len(args.categories) > len(args.values):
163
- args.values.extend([0.] * (len(args.categories) - len(args.values)))
166
+ args.values.extend([0.0] * (len(args.categories) - len(args.values)))
164
167
 
165
168
  builder = BarChartBuilder(labels_align_middle=args.labels_align_middle)
166
169
 
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com>
1
+ # Copyright (c) 2017 Egor Tensin <egor@tensin.name>
2
2
  # This file is part of the "VK scripts" project.
3
3
  # For details, see https://github.com/egor-tensin/vk-scripts.
4
4
  # Distributed under the MIT License.
@@ -59,13 +59,11 @@ _DEFAULT_ENCODING = 'utf-8'
59
59
 
60
60
 
61
61
  def open_input_text_file(path=None):
62
- return _open_file(path, default=sys.stdin, mode='r',
63
- encoding=_DEFAULT_ENCODING)
62
+ return _open_file(path, default=sys.stdin, mode='r', encoding=_DEFAULT_ENCODING)
64
63
 
65
64
 
66
65
  def open_output_text_file(path=None, mode='w'):
67
- return _open_file(path, default=sys.stdout, mode=mode,
68
- encoding=_DEFAULT_ENCODING)
66
+ return _open_file(path, default=sys.stdout, mode=mode, encoding=_DEFAULT_ENCODING)
69
67
 
70
68
 
71
69
  def open_output_binary_file(path=None):
@@ -11,5 +11,6 @@ except Exception:
11
11
 
12
12
 
13
13
  def add_to_arg_parser(parser):
14
- parser.add_argument('--version', '-V', action='version',
15
- version=f'%(prog)s {__version__}')
14
+ parser.add_argument(
15
+ '--version', '-V', action='version', version=f'%(prog)s {__version__}'
16
+ )
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vk_scripts
3
- Version: 1.0.4
3
+ Version: 1.0.5
4
4
  Summary: Scripts to stalk people on VK
5
- Author-email: Egor Tensin <Egor.Tensin@gmail.com>
5
+ Author-email: Egor Tensin <egor@tensin.name>
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/egor-tensin/vk-scripts
8
8
  Project-URL: Bug Tracker, https://github.com/egor-tensin/vk-scripts/issues
9
- Classifier: Development Status :: 4 - Beta
9
+ Classifier: Development Status :: 5 - Production/Stable
10
10
  Requires-Python: >=3.4
11
11
  Description-Content-Type: text/markdown
12
12
  License-File: LICENSE.txt
@@ -1,3 +1,4 @@
1
+ .git-blame-ignore-revs
1
2
  .gitattributes
2
3
  .gitignore
3
4
  .pylintrc
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes