skypro-cli-tool 0.1.1__tar.gz → 0.2.1__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 (49) hide show
  1. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/PKG-INFO +15 -7
  2. skypro_cli_tool-0.2.1/README.md +30 -0
  3. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/pyproject.toml +13 -1
  4. skypro_cli_tool-0.2.1/src/skypro/cli/app.py +55 -0
  5. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/cli/commands/summary.py +6 -14
  6. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/cli/renders/summary_table.py +4 -5
  7. skypro_cli_tool-0.2.1/src/skypro/config/__init__.py +18 -0
  8. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/config/logging.py +5 -7
  9. skypro_cli_tool-0.2.1/src/skypro/config/service.py +44 -0
  10. skypro_cli_tool-0.2.1/src/skypro/config/settings.py +55 -0
  11. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/domain/services.py +2 -3
  12. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/infra/api/skypro/client.py +3 -3
  13. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/infra/http/client.py +2 -2
  14. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/infra/repositories/summary.py +5 -2
  15. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/infra/repositories/work.py +1 -1
  16. skypro_cli_tool-0.2.1/src/skypro/logs/skypro.log +83 -0
  17. skypro_cli_tool-0.2.1/src/skypro/logs/skypro.log.1 +101 -0
  18. skypro_cli_tool-0.2.1/src/skypro/logs/skypro.log.2 +109 -0
  19. skypro_cli_tool-0.2.1/src/skypro/logs/skypro.log.3 +108 -0
  20. skypro_cli_tool-0.2.1/src/skypro/logs/skypro.log.4 +101 -0
  21. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/main.py +1 -1
  22. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/use_cases/get_summary_info.py +6 -3
  23. skypro_cli_tool-0.1.1/README.md +0 -22
  24. skypro_cli_tool-0.1.1/src/skypro/cli/app.py +0 -25
  25. skypro_cli_tool-0.1.1/src/skypro/config/settings.py +0 -29
  26. skypro_cli_tool-0.1.1/src/skypro/infra/http/__init__.py +0 -0
  27. skypro_cli_tool-0.1.1/src/skypro/logs/skypro.log +0 -11
  28. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/__init__.py +0 -0
  29. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/cli/__init__.py +0 -0
  30. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/cli/commands/__init__.py +0 -0
  31. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/cli/renders/__init__.py +0 -0
  32. {skypro_cli_tool-0.1.1/src/skypro/config → skypro_cli_tool-0.2.1/src/skypro/domain}/__init__.py +0 -0
  33. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/domain/enums.py +0 -0
  34. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/domain/errors.py +0 -0
  35. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/domain/models.py +0 -0
  36. {skypro_cli_tool-0.1.1/src/skypro/domain → skypro_cli_tool-0.2.1/src/skypro/infra}/__init__.py +0 -0
  37. {skypro_cli_tool-0.1.1/src/skypro/infra → skypro_cli_tool-0.2.1/src/skypro/infra/api}/__init__.py +0 -0
  38. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/infra/api/base.py +0 -0
  39. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/infra/api/errors.py +0 -0
  40. {skypro_cli_tool-0.1.1/src/skypro/infra/api → skypro_cli_tool-0.2.1/src/skypro/infra/api/skypro}/__init__.py +0 -0
  41. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/infra/api/skypro/dto.py +0 -0
  42. {skypro_cli_tool-0.1.1/src/skypro/infra/api/skypro → skypro_cli_tool-0.2.1/src/skypro/infra/http}/__init__.py +0 -0
  43. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/infra/http/errors.py +0 -0
  44. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/infra/repositories/__init__.py +0 -0
  45. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/infra/repositories/errors.py +0 -0
  46. /skypro_cli_tool-0.1.1/src/skypro/logs/skypro.log.1 → /skypro_cli_tool-0.2.1/src/skypro/logs/skypro.log.5 +0 -0
  47. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/use_cases/__init__.py +0 -0
  48. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/utils.py +0 -0
  49. {skypro_cli_tool-0.1.1 → skypro_cli_tool-0.2.1}/src/skypro/work_prices.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: skypro-cli-tool
3
- Version: 0.1.1
3
+ Version: 0.2.1
4
4
  Summary: SkyPro cli tool
5
5
  Requires-Dist: aiohttp>=3.13.5
6
6
  Requires-Dist: httpx>=0.28.1
@@ -14,21 +14,29 @@ Description-Content-Type: text/markdown
14
14
 
15
15
  ## How to start
16
16
 
17
- Set environment variables to `.bashrc`
17
+ Show current config
18
18
 
19
19
  ```commandline
20
- SKYPRO__EMAIL=ivan.ivanov@skyeng.ru
21
- SKYPRO__PASSWORD=secret-password
20
+ sky-cli config
22
21
  ```
23
22
 
24
- Check money you are earned this month
23
+ Reconfigure SkyPro access
25
24
 
26
25
  ```commandline
27
- sky-cli
26
+ sky-cli config --skypro
27
+ ```
28
+
29
+ If SkyPro credentials are missing, the `summary` command exits with an error.
30
+ Configure them first with `sky-cli config --skypro`.
31
+
32
+ Check money you earned this month
33
+
34
+ ```commandline
35
+ sky-cli summary
28
36
  ```
29
37
 
30
38
  Another month or year
31
39
 
32
40
  ```commandline
33
- sky-cli --month 3 --year 2026
41
+ sky-cli summary --month 3 --year 2026
34
42
  ```
@@ -0,0 +1,30 @@
1
+ # SkyPro cli tool
2
+
3
+ ## How to start
4
+
5
+ Show current config
6
+
7
+ ```commandline
8
+ sky-cli config
9
+ ```
10
+
11
+ Reconfigure SkyPro access
12
+
13
+ ```commandline
14
+ sky-cli config --skypro
15
+ ```
16
+
17
+ If SkyPro credentials are missing, the `summary` command exits with an error.
18
+ Configure them first with `sky-cli config --skypro`.
19
+
20
+ Check money you earned this month
21
+
22
+ ```commandline
23
+ sky-cli summary
24
+ ```
25
+
26
+ Another month or year
27
+
28
+ ```commandline
29
+ sky-cli summary --month 3 --year 2026
30
+ ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "skypro-cli-tool"
3
- version = "0.1.1"
3
+ version = "0.2.1"
4
4
  description = "SkyPro cli tool"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -13,6 +13,11 @@ dependencies = [
13
13
  ]
14
14
 
15
15
  [dependency-groups]
16
+ dev = [
17
+ { include-group = "test" },
18
+ { include-group = "lint" },
19
+ "ipython>=9.13.0",
20
+ ]
16
21
  test = [
17
22
  "freezegun>=1.5.5",
18
23
  "pytest>=9.0.3",
@@ -59,8 +64,15 @@ preview = true
59
64
  "ANN", # Missing type annotation
60
65
  "S101", # Use of `assert` detected
61
66
  "PLR6301", # Method could be a function, class method, or static method
67
+ "S105", # Possible hardcoded password
68
+ "S106", # Possible hardcoded password
62
69
  "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes
63
70
  "PLR2004", # Magic value used in comparison
71
+ "RUF069", # Unreliable floating point equality comparison
72
+ ]
73
+ "*/cli/app.py" = [
74
+ "FBT001", # Boolean-typed positional argument in function definition
75
+ "FBT003", # Boolean positional value in function call
64
76
  ]
65
77
 
66
78
  [tool.ruff.lint.isort]
@@ -0,0 +1,55 @@
1
+ import asyncio
2
+ import json
3
+
4
+ import typer
5
+ from rich.console import Console
6
+ from typer import Option
7
+
8
+ from skypro.config import (
9
+ configure_skypro_settings,
10
+ enable_console_logging,
11
+ get_settings,
12
+ is_skypro_configured,
13
+ )
14
+
15
+ from . import commands
16
+
17
+ app = typer.Typer()
18
+ console = Console()
19
+
20
+
21
+ @app.command(name='summary')
22
+ def get_summary(
23
+ year: int | None = Option(None, help='Год для расчета'),
24
+ month: int | None = Option(None, help='Месяц для расчета'),
25
+ verbose: bool = typer.Option(False, '-v', '--verbose'),
26
+ ) -> None:
27
+ """Расчет стоимости работ"""
28
+ if not is_skypro_configured():
29
+ console.print(
30
+ '[red]Настройки SkyPro не заданы. '
31
+ 'Укажите их командой `sky-cli config --skypro`.[/]'
32
+ )
33
+ raise typer.Exit(code=1)
34
+
35
+ if verbose:
36
+ enable_console_logging(console)
37
+
38
+ asyncio.run(commands.show_summary(console, year, month))
39
+
40
+
41
+ @app.command(name='config')
42
+ def configure(
43
+ skypro: bool = Option(
44
+ False,
45
+ '--skypro',
46
+ help='Изменить настройки доступа к SkyPro',
47
+ ),
48
+ ) -> None:
49
+ """Просмотр текущего конфига или настройка доступа к SkyPro"""
50
+ if skypro:
51
+ configure_skypro_settings(console)
52
+ return
53
+
54
+ settings = get_settings().model_dump(mode='json')
55
+ console.print(json.dumps(settings, ensure_ascii=False, indent=2))
@@ -1,5 +1,5 @@
1
1
  from calendar import IllegalMonthError
2
- from datetime import UTC, date, datetime
2
+ from datetime import UTC, datetime
3
3
 
4
4
  from click.exceptions import Exit
5
5
  from rich.console import Console
@@ -8,7 +8,6 @@ from skypro.cli.renders import render_summary_table
8
8
  from skypro.domain.errors import DomainError
9
9
  from skypro.infra.repositories.errors import RepositoryError
10
10
  from skypro.use_cases import get_summary_info
11
- from skypro.utils import get_month_range
12
11
 
13
12
 
14
13
  async def show_summary(
@@ -16,23 +15,16 @@ async def show_summary(
16
15
  year: int | None,
17
16
  month: int | None,
18
17
  ) -> None:
19
- try:
20
- start, end = resolve_period(year, month)
18
+ now = datetime.now(UTC).astimezone()
19
+ year = year or now.year
20
+ month = month or now.month
21
21
 
22
+ try:
22
23
  with console.status('[cyan]Расчет стоимости работ'):
23
- report, total, after_tax = await get_summary_info(start, end)
24
+ report, total, after_tax = await get_summary_info(year, month)
24
25
 
25
26
  table = render_summary_table(report, total, after_tax)
26
27
  console.print(table)
27
28
  except (RepositoryError, DomainError, IllegalMonthError) as e:
28
29
  console.print(f'[bold red]Error: {e!s}[/]')
29
30
  raise Exit(code=1) from e
30
-
31
-
32
- def resolve_period(year: int | None, month: int | None) -> tuple[date, date]:
33
- now = datetime.now(UTC).astimezone()
34
- target_year = year or now.year
35
- target_month = month or now.month
36
-
37
- start, end = get_month_range(target_year, target_month)
38
- return start, end
@@ -23,22 +23,21 @@ def render_summary_table(
23
23
  table = Table(
24
24
  Column('Тип', style='cyan'),
25
25
  Column('Количество', style='magenta', justify='center'),
26
+ Column('Стоимость', style='magenta', justify='right'),
26
27
  Column('Оплата', style='magenta', justify='right'),
27
28
  title='Статистика работ за месяц',
28
29
  )
29
30
 
30
31
  for item in report:
31
- if not item.quantity:
32
- continue
33
-
34
32
  table.add_row(
35
33
  work_type_translate_map[item.work_type],
36
34
  str(item.quantity),
37
35
  format_price(item.price),
36
+ format_price(item.total),
38
37
  )
39
38
 
40
39
  table.add_section()
41
- table.add_row('Итого', '', format_price(total))
42
- table.add_row('После уплаты налога', '', format_price(total_after_tax))
40
+ table.add_row('Итого', '', '', format_price(total))
41
+ table.add_row('После уплаты налога', '', '', format_price(total_after_tax))
43
42
 
44
43
  return table
@@ -0,0 +1,18 @@
1
+ from .logging import configure_logging, enable_console_logging
2
+ from .service import (
3
+ configure_skypro_settings,
4
+ ensure_skypro_configured,
5
+ is_skypro_configured,
6
+ )
7
+ from .settings import BASE_DIR, get_settings, save_settings
8
+
9
+ __all__ = (
10
+ 'BASE_DIR',
11
+ 'configure_logging',
12
+ 'configure_skypro_settings',
13
+ 'enable_console_logging',
14
+ 'ensure_skypro_configured',
15
+ 'get_settings',
16
+ 'is_skypro_configured',
17
+ 'save_settings',
18
+ )
@@ -5,9 +5,9 @@ from functools import cache
5
5
  from rich.console import Console
6
6
  from rich.logging import RichHandler
7
7
 
8
- from .settings import BASE_DIR, settings
8
+ from .settings import HOME_DIR, get_settings
9
9
 
10
- LOG_DIR = BASE_DIR / 'logs'
10
+ LOG_DIR = HOME_DIR / 'logs'
11
11
 
12
12
  LOGGING_CONFIG = {
13
13
  'version': 1,
@@ -27,10 +27,7 @@ LOGGING_CONFIG = {
27
27
  'formatter': 'formatter',
28
28
  },
29
29
  },
30
- 'root': {
31
- 'handlers': ['file'],
32
- 'level': settings.log_level,
33
- },
30
+ 'root': {'handlers': ['file']},
34
31
  'loggers': {
35
32
  'httpx': {'handlers': ['file'], 'level': logging.WARNING, 'propagate': True},
36
33
  },
@@ -39,7 +36,8 @@ LOGGING_CONFIG = {
39
36
 
40
37
  @cache
41
38
  def configure_logging() -> None:
42
- LOG_DIR.mkdir(exist_ok=True)
39
+ LOG_DIR.mkdir(mode=0o700, exist_ok=True, parents=True)
40
+ LOGGING_CONFIG['root']['level'] = get_settings().log_level.upper()
43
41
  logging.config.dictConfig(LOGGING_CONFIG)
44
42
 
45
43
 
@@ -0,0 +1,44 @@
1
+ from pydantic import SecretStr
2
+ from rich.console import Console
3
+
4
+ from .settings import Settings, get_settings, save_settings
5
+
6
+
7
+ def is_skypro_configured(settings: Settings | None = None) -> bool:
8
+ current_settings = settings or get_settings()
9
+ return bool(current_settings.skypro.email and current_settings.skypro.password)
10
+
11
+
12
+ def configure_skypro_settings(console: Console) -> Settings:
13
+ current_settings = get_settings()
14
+
15
+ console.print('[cyan]Настройка доступа к SkyPro[/]')
16
+
17
+ current_email = current_settings.skypro.email or ''
18
+ email_prompt = 'Email'
19
+ if current_email:
20
+ email_prompt = f'Email [{current_email}]'
21
+
22
+ email = console.input(f'{email_prompt}: ').strip() or current_email
23
+ password = console.input('Password: ', password=True).strip()
24
+ if not password and current_settings.skypro.password.get_secret_value():
25
+ password = current_settings.skypro.password.get_secret_value()
26
+
27
+ updated_settings = current_settings.model_copy(
28
+ update={
29
+ 'skypro': current_settings.skypro.model_copy(
30
+ update={'email': email, 'password': SecretStr(password)}
31
+ )
32
+ }
33
+ )
34
+ save_settings(updated_settings)
35
+ return updated_settings
36
+
37
+
38
+ def ensure_skypro_configured(console: Console) -> Settings:
39
+ settings = get_settings()
40
+ if is_skypro_configured(settings):
41
+ return settings
42
+
43
+ console.print('[yellow]Конфигурация SkyPro не найдена. Заполним ее сейчас.[/]')
44
+ return configure_skypro_settings(console)
@@ -0,0 +1,55 @@
1
+ import json
2
+ from functools import cache
3
+ from pathlib import Path
4
+ from typing import Literal
5
+
6
+ from pydantic import BaseModel, Field, SecretStr
7
+ from pydantic_settings import (
8
+ BaseSettings,
9
+ JsonConfigSettingsSource,
10
+ PydanticBaseSettingsSource,
11
+ )
12
+
13
+ BASE_DIR = Path(__file__).resolve().parent.parent
14
+
15
+ HOME_DIR = Path.home() / '.skypro'
16
+
17
+ SETTINGS_FILE_PATH = HOME_DIR / 'settings.json'
18
+
19
+
20
+ class SkyProSettings(BaseModel):
21
+ email: str | None = None
22
+ password: SecretStr | None = None
23
+
24
+
25
+ class Settings(BaseSettings, json_file=SETTINGS_FILE_PATH):
26
+ tax_percent: float = 6.0
27
+ default_request_timeout: int = 3
28
+ log_level: Literal['debug', 'info'] = 'info'
29
+
30
+ skypro: SkyProSettings = Field(default_factory=SkyProSettings)
31
+
32
+ @classmethod
33
+ def settings_customise_sources(
34
+ cls,
35
+ settings_cls: type[BaseSettings],
36
+ **_: PydanticBaseSettingsSource,
37
+ ) -> tuple[PydanticBaseSettingsSource, ...]:
38
+ return (JsonConfigSettingsSource(settings_cls),)
39
+
40
+
41
+ def save_settings(settings: Settings) -> None:
42
+ SETTINGS_FILE_PATH.parent.mkdir(mode=0o700, exist_ok=True)
43
+
44
+ data = settings.model_dump(mode='json')
45
+ data['skypro']['password'] = settings.skypro.password.get_secret_value()
46
+
47
+ with SETTINGS_FILE_PATH.open(mode='w', encoding='utf-8') as f:
48
+ json.dump(data, f, ensure_ascii=False, indent=2)
49
+
50
+ get_settings.cache_clear()
51
+
52
+
53
+ @cache
54
+ def get_settings() -> Settings:
55
+ return Settings()
@@ -1,6 +1,5 @@
1
1
  from decimal import Decimal
2
2
 
3
- from skypro.config.settings import settings
4
3
  from skypro.domain.errors import MissingWorkPriceError
5
4
  from skypro.domain.models import WorkPrice, WorkReportItem, WorkSummary
6
5
 
@@ -32,6 +31,6 @@ def calculate_total(report: list[WorkReportItem]) -> Decimal:
32
31
  return sum((item.total for item in report), Decimal(0))
33
32
 
34
33
 
35
- def apply_tax(amount: Decimal) -> Decimal:
36
- tax_rate = Decimal(str(settings.tax_percent)) / Decimal(100)
34
+ def apply_tax(amount: Decimal, tax_percent: float) -> Decimal:
35
+ tax_rate = Decimal(str(tax_percent)) / Decimal(100)
37
36
  return amount * (Decimal(1) - tax_rate)
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
  from datetime import date
3
3
 
4
- from skypro.config.settings import settings
4
+ from skypro.config import get_settings
5
5
  from skypro.infra.api.base import BaseApiClient
6
6
  from skypro.infra.api.errors import AuthenticationError
7
7
  from skypro.infra.http.client import HttpClient
@@ -52,8 +52,8 @@ class SkyProClient(BaseApiClient):
52
52
  response = await self._post(
53
53
  self.login_url,
54
54
  data={
55
- 'email': settings.skypro.email,
56
- 'password': settings.skypro.password,
55
+ 'email': get_settings().skypro.email,
56
+ 'password': get_settings().skypro.password.get_secret_value(),
57
57
  'csrfmiddlewaretoken': csrf_token,
58
58
  },
59
59
  headers={'Referer': str(self.cookies)},
@@ -5,7 +5,7 @@ from typing import Any, Self
5
5
  import httpx
6
6
  from httpx import AsyncClient, Request, Response
7
7
 
8
- from skypro.config.settings import settings
8
+ from skypro.config import get_settings
9
9
 
10
10
  from .errors import HttpError
11
11
 
@@ -15,7 +15,7 @@ logger = logging.getLogger(__name__)
15
15
  class HttpClient:
16
16
  def __init__(self, timeout: int | None = None) -> None:
17
17
  self._client = AsyncClient(
18
- timeout=timeout or settings.default_request_timeout,
18
+ timeout=timeout or get_settings().default_request_timeout,
19
19
  follow_redirects=True,
20
20
  event_hooks={'request': [_log_request], 'response': [_log_response]},
21
21
  )
@@ -4,7 +4,7 @@ from pydantic import ValidationError
4
4
 
5
5
  from skypro.domain.enums import WorkType
6
6
  from skypro.domain.models import WorkSummary
7
- from skypro.infra.api.errors import ApiError
7
+ from skypro.infra.api.errors import ApiError, AuthenticationError
8
8
  from skypro.infra.api.skypro.client import SkyProClient
9
9
  from skypro.infra.http.client import HttpClient
10
10
  from skypro.infra.http.errors import HttpError
@@ -16,8 +16,11 @@ async def get_summary(start_date: date, end_date: date) -> list[WorkSummary]:
16
16
  async with HttpClient() as http_client:
17
17
  skypro = SkyProClient(http_client)
18
18
  account_data = await skypro.get_account_data(start_date, end_date)
19
+ except AuthenticationError as e:
20
+ message = 'SkyPro authentication failed. Check email and password.'
21
+ raise SummaryLoadError(message) from e
19
22
  except (HttpError, ApiError, ValidationError) as e:
20
- raise SummaryLoadError from e
23
+ raise SummaryLoadError(str(e) or 'Failed to load summary from SkyPro.') from e
21
24
 
22
25
  return [
23
26
  WorkSummary(WorkType.HOMEWORK, account_data.services_summary.homework),
@@ -1,7 +1,7 @@
1
1
  import json
2
2
  from decimal import Decimal, InvalidOperation
3
3
 
4
- from skypro.config.settings import BASE_DIR
4
+ from skypro.config import BASE_DIR
5
5
  from skypro.domain.enums import WorkType
6
6
  from skypro.domain.models import WorkPrice
7
7
 
@@ -0,0 +1,83 @@
1
+ 2026-05-10 16:59:23 - INFO - SkyPro authentication successful
2
+ 2026-05-10 16:59:23 - INFO - Fetching account data from 2026-04-01 to 2026-04-30
3
+ 2026-05-10 16:59:23 - INFO - HTTP request: GET https://operation-planning.sky.pro/mentor-cabinet/api/data/?start_date=2026-04-01&end_date=2026-04-30
4
+ 2026-05-10 16:59:23 - INFO - HTTP response: GET https://operation-planning.sky.pro/mentor-cabinet/api/data/?start_date=2026-04-01&end_date=2026-04-30 -> 200
5
+ 2026-05-10 16:59:28 - INFO - Authenticating in SkyPro
6
+ 2026-05-10 16:59:28 - INFO - HTTP request: GET https://operation-planning.sky.pro/careusers/login/
7
+ 2026-05-10 16:59:28 - INFO - HTTP response: GET https://operation-planning.sky.pro/careusers/login/ -> 200
8
+ 2026-05-10 16:59:28 - INFO - HTTP request: POST https://operation-planning.sky.pro/careusers/login/
9
+ 2026-05-10 16:59:28 - INFO - HTTP response: POST https://operation-planning.sky.pro/careusers/login/ -> 302
10
+ 2026-05-10 16:59:28 - INFO - HTTP request: GET https://operation-planning.sky.pro/mentor-cabinet/
11
+ 2026-05-10 16:59:29 - INFO - HTTP response: GET https://operation-planning.sky.pro/mentor-cabinet/ -> 200
12
+ 2026-05-10 16:59:29 - INFO - SkyPro authentication successful
13
+ 2026-05-10 16:59:29 - INFO - Fetching account data from 2026-04-01 to 2026-04-30
14
+ 2026-05-10 16:59:29 - INFO - HTTP request: GET https://operation-planning.sky.pro/mentor-cabinet/api/data/?start_date=2026-04-01&end_date=2026-04-30
15
+ 2026-05-10 16:59:29 - INFO - HTTP response: GET https://operation-planning.sky.pro/mentor-cabinet/api/data/?start_date=2026-04-01&end_date=2026-04-30 -> 200
16
+ 2026-05-10 17:01:19 - INFO - Authenticating in SkyPro
17
+ 2026-05-10 17:01:19 - INFO - HTTP request: GET https://operation-planning.sky.pro/careusers/login/
18
+ 2026-05-10 17:01:20 - INFO - HTTP response: GET https://operation-planning.sky.pro/careusers/login/ -> 200
19
+ 2026-05-10 17:01:20 - INFO - HTTP request: POST https://operation-planning.sky.pro/careusers/login/
20
+ 2026-05-10 17:01:20 - INFO - HTTP response: POST https://operation-planning.sky.pro/careusers/login/ -> 302
21
+ 2026-05-10 17:01:20 - INFO - HTTP request: GET https://operation-planning.sky.pro/mentor-cabinet/
22
+ 2026-05-10 17:01:20 - INFO - HTTP response: GET https://operation-planning.sky.pro/mentor-cabinet/ -> 200
23
+ 2026-05-10 17:01:20 - INFO - SkyPro authentication successful
24
+ 2026-05-10 17:01:20 - INFO - Fetching account data from 2026-04-01 to 2026-04-30
25
+ 2026-05-10 17:01:20 - INFO - HTTP request: GET https://operation-planning.sky.pro/mentor-cabinet/api/data/?start_date=2026-04-01&end_date=2026-04-30
26
+ 2026-05-10 17:01:21 - INFO - HTTP response: GET https://operation-planning.sky.pro/mentor-cabinet/api/data/?start_date=2026-04-01&end_date=2026-04-30 -> 200
27
+ 2026-05-10 17:01:25 - INFO - Authenticating in SkyPro
28
+ 2026-05-10 17:01:25 - INFO - HTTP request: GET https://operation-planning.sky.pro/careusers/login/
29
+ 2026-05-10 17:01:25 - INFO - HTTP response: GET https://operation-planning.sky.pro/careusers/login/ -> 200
30
+ 2026-05-10 17:01:25 - INFO - HTTP request: POST https://operation-planning.sky.pro/careusers/login/
31
+ 2026-05-10 17:01:25 - INFO - HTTP response: POST https://operation-planning.sky.pro/careusers/login/ -> 302
32
+ 2026-05-10 17:01:25 - INFO - HTTP request: GET https://operation-planning.sky.pro/careusers/login/
33
+ 2026-05-10 17:01:25 - INFO - HTTP response: GET https://operation-planning.sky.pro/careusers/login/ -> 200
34
+ 2026-05-10 17:01:40 - INFO - Authenticating in SkyPro
35
+ 2026-05-10 17:01:40 - INFO - HTTP request: GET https://operation-planning.sky.pro/careusers/login/
36
+ 2026-05-10 17:01:41 - INFO - HTTP response: GET https://operation-planning.sky.pro/careusers/login/ -> 200
37
+ 2026-05-10 17:01:41 - INFO - HTTP request: POST https://operation-planning.sky.pro/careusers/login/
38
+ 2026-05-10 17:01:41 - INFO - HTTP response: POST https://operation-planning.sky.pro/careusers/login/ -> 302
39
+ 2026-05-10 17:01:41 - INFO - HTTP request: GET https://operation-planning.sky.pro/mentor-cabinet/
40
+ 2026-05-10 17:01:41 - INFO - HTTP response: GET https://operation-planning.sky.pro/mentor-cabinet/ -> 200
41
+ 2026-05-10 17:01:41 - INFO - SkyPro authentication successful
42
+ 2026-05-10 17:01:41 - INFO - Fetching account data from 2026-04-01 to 2026-04-30
43
+ 2026-05-10 17:01:41 - INFO - HTTP request: GET https://operation-planning.sky.pro/mentor-cabinet/api/data/?start_date=2026-04-01&end_date=2026-04-30
44
+ 2026-05-10 17:01:42 - INFO - HTTP response: GET https://operation-planning.sky.pro/mentor-cabinet/api/data/?start_date=2026-04-01&end_date=2026-04-30 -> 200
45
+ 2026-05-10 17:03:18 - INFO - Authenticating in SkyPro
46
+ 2026-05-10 17:03:18 - INFO - HTTP request: GET https://operation-planning.sky.pro/careusers/login/
47
+ 2026-05-10 17:03:18 - INFO - HTTP response: GET https://operation-planning.sky.pro/careusers/login/ -> 200
48
+ 2026-05-10 17:03:18 - INFO - HTTP request: POST https://operation-planning.sky.pro/careusers/login/
49
+ 2026-05-10 17:03:19 - INFO - HTTP response: POST https://operation-planning.sky.pro/careusers/login/ -> 302
50
+ 2026-05-10 17:03:19 - INFO - HTTP request: GET https://operation-planning.sky.pro/mentor-cabinet/
51
+ 2026-05-10 17:03:19 - INFO - HTTP response: GET https://operation-planning.sky.pro/mentor-cabinet/ -> 200
52
+ 2026-05-10 17:03:19 - INFO - SkyPro authentication successful
53
+ 2026-05-10 17:03:19 - INFO - Fetching account data from 2026-04-01 to 2026-04-30
54
+ 2026-05-10 17:03:19 - INFO - HTTP request: GET https://operation-planning.sky.pro/mentor-cabinet/api/data/?start_date=2026-04-01&end_date=2026-04-30
55
+ 2026-05-10 17:03:19 - INFO - HTTP response: GET https://operation-planning.sky.pro/mentor-cabinet/api/data/?start_date=2026-04-01&end_date=2026-04-30 -> 200
56
+ 2026-05-10 17:14:09 - INFO - Authenticating in SkyPro
57
+ 2026-05-10 17:14:09 - INFO - HTTP request: GET https://operation-planning.sky.pro/careusers/login/
58
+ 2026-05-10 17:14:10 - INFO - HTTP response: GET https://operation-planning.sky.pro/careusers/login/ -> 200
59
+ 2026-05-10 17:14:10 - INFO - HTTP request: POST https://operation-planning.sky.pro/careusers/login/
60
+ 2026-05-10 17:14:10 - INFO - HTTP response: POST https://operation-planning.sky.pro/careusers/login/ -> 302
61
+ 2026-05-10 17:14:10 - INFO - HTTP request: GET https://operation-planning.sky.pro/careusers/login/
62
+ 2026-05-10 17:14:10 - INFO - HTTP response: GET https://operation-planning.sky.pro/careusers/login/ -> 200
63
+ 2026-05-10 17:14:31 - INFO - Authenticating in SkyPro
64
+ 2026-05-10 17:14:31 - INFO - HTTP request: GET https://operation-planning.sky.pro/careusers/login/
65
+ 2026-05-10 17:14:32 - INFO - HTTP response: GET https://operation-planning.sky.pro/careusers/login/ -> 200
66
+ 2026-05-10 17:14:32 - INFO - HTTP request: POST https://operation-planning.sky.pro/careusers/login/
67
+ 2026-05-10 17:14:33 - INFO - HTTP response: POST https://operation-planning.sky.pro/careusers/login/ -> 302
68
+ 2026-05-10 17:14:33 - INFO - HTTP request: GET https://operation-planning.sky.pro/mentor-cabinet/
69
+ 2026-05-10 17:14:33 - INFO - HTTP response: GET https://operation-planning.sky.pro/mentor-cabinet/ -> 200
70
+ 2026-05-10 17:14:33 - INFO - SkyPro authentication successful
71
+ 2026-05-10 17:14:33 - INFO - Fetching account data from 2026-04-01 to 2026-04-30
72
+ 2026-05-10 17:14:33 - INFO - HTTP request: GET https://operation-planning.sky.pro/mentor-cabinet/api/data/?start_date=2026-04-01&end_date=2026-04-30
73
+ 2026-05-10 17:14:33 - INFO - HTTP response: GET https://operation-planning.sky.pro/mentor-cabinet/api/data/?start_date=2026-04-01&end_date=2026-04-30 -> 200
74
+ 2026-05-10 17:20:00 - INFO - Authenticating in SkyPro
75
+ 2026-05-10 17:20:00 - INFO - HTTP request: GET https://operation-planning.sky.pro/careusers/login/
76
+ 2026-05-10 17:38:44 - INFO - Authenticating in SkyPro
77
+ 2026-05-10 17:38:44 - INFO - HTTP request: GET https://operation-planning.sky.pro/careusers/login/
78
+ 2026-05-10 17:38:45 - INFO - HTTP response: GET https://operation-planning.sky.pro/careusers/login/ -> 200
79
+ 2026-05-10 17:38:45 - INFO - HTTP request: POST https://operation-planning.sky.pro/careusers/login/
80
+ 2026-05-10 17:38:45 - INFO - HTTP response: POST https://operation-planning.sky.pro/careusers/login/ -> 302
81
+ 2026-05-10 17:38:45 - INFO - HTTP request: GET https://operation-planning.sky.pro/mentor-cabinet/
82
+ 2026-05-10 17:38:45 - INFO - HTTP response: GET https://operation-planning.sky.pro/mentor-cabinet/ -> 200
83
+ 2026-05-10 17:38:45 - INFO - SkyPro authentication successful