make this project a poetry project

master
Maximilian Zettler 4 years ago
parent a17b6ce828
commit 645e3ef5a8
  1. 6
      README.md
  2. 286
      poetry.lock
  3. 19
      pyproject.toml
  4. 0
      tests/__init__.py
  5. 5
      tests/test_timebot.py
  6. 1
      timebot.ini.ex
  7. 1
      timebot/__init__.py
  8. 116
      timebot/app.py
  9. 0
      timebot/constants.py
  10. 128
      timebot/timebot.py

@ -2,6 +2,12 @@
A simple command line utility to make mobatime time entries.
# Development
This is a poetry managed project. For details see:
* https://python-poetry.org/docs/#installation
* https://python-poetry.org/docs/basic-usage/
# ERRORS
## SSL verify error

286
poetry.lock generated

@ -0,0 +1,286 @@
[[package]]
name = "atomicwrites"
version = "1.4.0"
description = "Atomic file writes."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "attrs"
version = "21.4.0"
description = "Classes Without Boilerplate"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.extras]
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"]
docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
[[package]]
name = "certifi"
version = "2021.10.8"
description = "Python package for providing Mozilla's CA Bundle."
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "charset-normalizer"
version = "2.0.11"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "main"
optional = false
python-versions = ">=3.5.0"
[package.extras]
unicode_backport = ["unicodedata2"]
[[package]]
name = "colorama"
version = "0.4.4"
description = "Cross-platform colored terminal text."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "idna"
version = "3.3"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "main"
optional = false
python-versions = ">=3.5"
[[package]]
name = "importlib-metadata"
version = "4.8.3"
description = "Read metadata from Python packages"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
zipp = ">=0.5"
[package.extras]
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
perf = ["ipython"]
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
[[package]]
name = "more-itertools"
version = "8.12.0"
description = "More routines for operating on iterables, beyond itertools"
category = "dev"
optional = false
python-versions = ">=3.5"
[[package]]
name = "packaging"
version = "21.3"
description = "Core utilities for Python packages"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
[[package]]
name = "pluggy"
version = "0.13.1"
description = "plugin and hook calling mechanisms for python"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[package.dependencies]
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
[package.extras]
dev = ["pre-commit", "tox"]
[[package]]
name = "py"
version = "1.11.0"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "pyparsing"
version = "3.0.7"
description = "Python parsing module"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.extras]
diagrams = ["jinja2", "railroad-diagrams"]
[[package]]
name = "pytest"
version = "5.4.3"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
python-versions = ">=3.5"
[package.dependencies]
atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
attrs = ">=17.4.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
more-itertools = ">=4.0.0"
packaging = "*"
pluggy = ">=0.12,<1.0"
py = ">=1.5.0"
wcwidth = "*"
[package.extras]
checkqa-mypy = ["mypy (==v0.761)"]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
[[package]]
name = "requests"
version = "2.27.1"
description = "Python HTTP for Humans."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
urllib3 = ">=1.21.1,<1.27"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
[[package]]
name = "typing-extensions"
version = "4.0.1"
description = "Backported and Experimental Type Hints for Python 3.6+"
category = "dev"
optional = false
python-versions = ">=3.6"
[[package]]
name = "urllib3"
version = "1.26.8"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
[package.extras]
brotli = ["brotlipy (>=0.6.0)"]
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "wcwidth"
version = "0.2.5"
description = "Measures the displayed width of unicode strings in a terminal"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "zipp"
version = "3.6.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.extras]
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
[metadata]
lock-version = "1.1"
python-versions = "^3.6"
content-hash = "90a881b9873674d1beb1cbae32c605d019b3f2a945780d9fff9f98f2ad26aa02"
[metadata.files]
atomicwrites = [
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
]
attrs = [
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
]
certifi = [
{file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
{file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
]
charset-normalizer = [
{file = "charset-normalizer-2.0.11.tar.gz", hash = "sha256:98398a9d69ee80548c762ba991a4728bfc3836768ed226b3945908d1a688371c"},
{file = "charset_normalizer-2.0.11-py3-none-any.whl", hash = "sha256:2842d8f5e82a1f6aa437380934d5e1cd4fcf2003b06fed6940769c164a480a45"},
]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
]
idna = [
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
]
importlib-metadata = [
{file = "importlib_metadata-4.8.3-py3-none-any.whl", hash = "sha256:65a9576a5b2d58ca44d133c42a241905cc45e34d2c06fd5ba2bafa221e5d7b5e"},
{file = "importlib_metadata-4.8.3.tar.gz", hash = "sha256:766abffff765960fcc18003801f7044eb6755ffae4521c8e8ce8e83b9c9b0668"},
]
more-itertools = [
{file = "more-itertools-8.12.0.tar.gz", hash = "sha256:7dc6ad46f05f545f900dd59e8dfb4e84a4827b97b3cfecb175ea0c7d247f6064"},
{file = "more_itertools-8.12.0-py3-none-any.whl", hash = "sha256:43e6dd9942dffd72661a2c4ef383ad7da1e6a3e968a927ad7a6083ab410a688b"},
]
packaging = [
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
]
pluggy = [
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
]
py = [
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
]
pyparsing = [
{file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"},
{file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"},
]
pytest = [
{file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"},
{file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"},
]
requests = [
{file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
{file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
]
typing-extensions = [
{file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"},
{file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"},
]
urllib3 = [
{file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"},
{file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
]
wcwidth = [
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
{file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
]
zipp = [
{file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"},
{file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"},
]

@ -0,0 +1,19 @@
[tool.poetry]
name = "timebot"
version = "0.1.0"
description = ""
authors = ["Maximilian Zettler <info@max-zettler.de>"]
[tool.poetry.scripts]
timebot = 'timebot.app:run'
[tool.poetry.dependencies]
python = "^3.6"
requests = "^2.27.1"
[tool.poetry.dev-dependencies]
pytest = "^5.2"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

@ -0,0 +1,5 @@
from timebot import __version__
def test_version():
assert __version__ == '0.1.0'

@ -1,2 +1,3 @@
[general]
baseurl=https://host/mobatime/
system_ca_store=/etc/ssl/certs/ca-certificates.crt

@ -0,0 +1 @@
__version__ = '0.1.0'

@ -0,0 +1,116 @@
import argparse
import configparser
import datetime
import logging
import os
import sys
from timebot.constants import SIMPLE_DATETIME_FORMAT_HUMAN, PUNCH_COMMANDS, SIMPLE_DATETIME_FORMAT, SIMPLE_DATE_FORMAT, \
SIMPLE_TIME_FORMAT
from timebot.timebot import MobatimeApi, TimeBot
logger = logging.getLogger()
logging.basicConfig(level=logging.INFO)
def run():
parser = argparse.ArgumentParser()
parser.add_argument("-v", help="enable debug logging", action="store_true")
parser.add_argument("-u", help="mobatime login user", required=True)
parser.add_argument("-p", help="mobatime login user password", default=None)
parser.add_argument("-c", help="config file", default="timebot.ini")
parser.add_argument("-a", "--use-system-ca-store", help="use system ca store for ssl connections",
action="store_true")
parser.add_argument("--save-cookies", help="save auth cookies to `./.kekse`", action="store_true", default=False)
subparsers = parser.add_subparsers(help='sub-command help', dest='subparser_name')
# subparser command: `status`
parser_status = subparsers.add_parser("status", help="show your current tracking status")
# subparser command: `list-entries`
parser_list_entries = subparsers.add_parser("list-entries", help="use this command to list your time entries")
parser_list_entries.add_argument("--start-date",
help=f"start date filter in format `{SIMPLE_DATETIME_FORMAT_HUMAN}` "
f"(default: now - 10days; unset for default)")
parser_list_entries.add_argument("--end-date",
help=f"end date filter in format `{SIMPLE_DATETIME_FORMAT_HUMAN}` "
f"(default: now; unset for default)")
parser_list_entries.add_argument("--items", help="max items to request per page", default=20, type=int)
# subparser command: `punch`
parser_punch = subparsers.add_parser("punch",
help="use this command to punch in, punch out, or create break entries")
parser_punch.add_argument("-t",
help=f"type of time entry; this can be {', '.join(PUNCH_COMMANDS)}",
default="punch_in",
choices=PUNCH_COMMANDS)
parser_punch.add_argument("-s", help=f"timestamp in format `{SIMPLE_DATETIME_FORMAT_HUMAN}` or `now`",
default="now")
# subparser command: `smart-punch`
parser_smart_punch = subparsers.add_parser("smart-punch",
help="use this command to auto punch in, punch out and create break "
"entries; this command tries to detect your last action and will "
"create entries in to following order: "
"punch_in -> break_start -> break_end -> punch_out")
parser_smart_punch.add_argument("-s", help=f"timestamp in format `{SIMPLE_DATETIME_FORMAT_HUMAN}` or `now`",
default="now")
args = parser.parse_args()
if args.v:
logger.setLevel(logging.DEBUG)
config = configparser.ConfigParser()
config.read(args.c)
if args.use_system_ca_store:
os.environ["REQUESTS_CA_BUNDLE"] = config["general"]["system_ca_store"]\
if "system_ca_store" in config["general"] else "/etc/ssl/certs/ca-certificates.crt"
ma = MobatimeApi(baseurl=config["general"]["baseurl"], user=args.u, password=args.p, ask_for_password=True,
save_session=args.save_cookies)
tb = TimeBot(mobatime_api=ma)
if args.subparser_name == "punch":
if args.s == "now":
punch_datetime = datetime.datetime.now()
else:
punch_datetime = datetime.datetime.strptime(args.s, SIMPLE_DATETIME_FORMAT)
logger.info("running `{}` with date `{}` and time `{}`".format(
args.t,
punch_datetime.strftime(SIMPLE_DATE_FORMAT),
punch_datetime.strftime(SIMPLE_TIME_FORMAT),
))
getattr(tb, args.t)(punch_datetime)
elif args.subparser_name == "smart-punch":
if args.s == "now":
punch_datetime = datetime.datetime.now()
else:
punch_datetime = datetime.datetime.strptime(args.s, SIMPLE_DATETIME_FORMAT)
tb.smart_punch(punch_datetime)
elif args.subparser_name == "list-entries":
end_date = None
if args.end_date:
end_date = datetime.datetime.strptime(args.end_date, SIMPLE_DATETIME_FORMAT)
start_date = None
if args.start_date:
start_date = datetime.datetime.strptime(args.start_date, SIMPLE_DATETIME_FORMAT)
data = tb.mobatime_api.get_entries(entries=args.items, start_date=start_date, end_date=end_date)
data.reverse()
for i in data:
print("Entry: {} - DateTime: {} - Note: {}".format(i["entryName"], i["dateTime"], i["note"]))
elif args.subparser_name == "status":
tracking_data = tb.mobatime_api.get_tracking_data()
account_info = tb.mobatime_api.get_account_information()
print("Tracking Info:")
print(f" Current State: {tracking_data['actualState']}")
print(f" Last Booking: {tracking_data['lastBooking']}")
print(f" Last Booking Date: {tracking_data['lastBookingDate']}")
print("Account Infos:")
for i in account_info:
print(f" {i['accountName']}: {i['value']}")
else:
logger.error("Noting done... dunno what you want!")
sys.exit(1)
sys.exit(0)

@ -1,5 +1,3 @@
import argparse
import configparser
import datetime
import getpass
import logging
@ -8,12 +6,9 @@ import sys
import requests as requests
from constants import COMING_ENTRY_CODE_ID, LEAVING_ENTRY_CODE_ID, PUNCH_COMMANDS, BREAK_START_ENTRY_CODE_ID, \
from timebot.constants import COMING_ENTRY_CODE_ID, LEAVING_ENTRY_CODE_ID, BREAK_START_ENTRY_CODE_ID, \
BREAK_END_ENTRY_CODE_ID, SIMPLE_DATE_FORMAT, SIMPLE_TIME_FORMAT, SIMPLE_DATETIME_FORMAT, \
SIMPLE_DATETIME_FORMAT_HUMAN, USER_AGENT
logger = logging.getLogger()
logging.basicConfig(level=logging.INFO)
USER_AGENT
class MobatimeApi:
@ -49,9 +44,9 @@ class MobatimeApi:
try:
self._load_session_cookies(self._session)
except FileNotFoundError as e:
logger.warning(e) # file does not exist... ignored
self.logger.warning(e) # file does not exist... ignored
except (EOFError, pickle.UnpicklingError) as e:
logger.warning(e) # file seems to be corrupt... ignoring
self.logger.warning(e) # file seems to be corrupt... ignoring
request = self._session.get(self.baseurl + "Employee/GetEmployeeList")
if 400 <= request.status_code < 500:
self.logger.debug(f"got error {request.status_code}... trying to log in")
@ -271,127 +266,30 @@ class TimeBot:
self.mobatime_api.save_entry(punch_datetime, BREAK_END_ENTRY_CODE_ID, note="pause ende").raise_for_status()
def smart_punch(self, punch_datetime):
last_punch = tb.mobatime_api.get_entries(1, punch_datetime.replace(hour=0, minute=0, second=0, microsecond=0))
last_punch = self.mobatime_api.get_entries(1, punch_datetime.replace(hour=0, minute=0, second=0, microsecond=0))
method = None
if not last_punch or last_punch[0]["entryNumber"] is None:
logger.debug("could not detect any time entry for today... punching in")
self.logger.debug("could not detect any time entry for today... punching in")
method = "punch_in"
elif last_punch[0]["entryNumber"] == COMING_ENTRY_CODE_ID:
logger.debug("your last entry was `punch_in`... starting break")
self.logger.debug("your last entry was `punch_in`... starting break")
method = "break_start"
elif last_punch[0]["entryNumber"] == BREAK_START_ENTRY_CODE_ID:
logger.debug("your last entry was `break_start`... ending break")
self.logger.debug("your last entry was `break_start`... ending break")
method = "break_end"
elif last_punch[0]["entryNumber"] == BREAK_END_ENTRY_CODE_ID:
logger.debug("your last entry was `break_end`... punching out")
self.logger.debug("your last entry was `break_end`... punching out")
method = "punch_out"
elif last_punch[0]["entryNumber"] == LEAVING_ENTRY_CODE_ID:
logger.error("your last entry was `punch_out`... punching in again with this command is not supported")
self.logger.error("your last entry was `punch_out`... punching in again with this command is not supported")
sys.exit(1)
if method is None:
logger.error("hit an unknown situation... detection failed; run with `-v` for more info")
logger.debug(f"last entry was: {last_punch}")
self.logger.error("hit an unknown situation... detection failed; run with `-v` for more info")
self.logger.debug(f"last entry was: {last_punch}")
exit(1)
logger.info("running `{}` with date `{}` and time `{}`".format(
self.logger.info("running `{}` with date `{}` and time `{}`".format(
method,
punch_datetime.strftime(SIMPLE_DATE_FORMAT),
punch_datetime.strftime(SIMPLE_TIME_FORMAT),
))
getattr(self, method)(punch_datetime)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("-v", help="enable debug logging", action="store_true")
parser.add_argument("-u", help="mobatime login user", required=True)
parser.add_argument("-p", help="mobatime login user password", default=None)
parser.add_argument("-c", help="config file", default="timebot.ini")
parser.add_argument("--save-cookies", help="save auth cookies to `./.kekse`", action="store_true", default=False)
subparsers = parser.add_subparsers(help='sub-command help', dest='subparser_name')
# subparser command: `status`
parser_status = subparsers.add_parser("status", help="show your current tracking status")
# subparser command: `list-entries`
parser_list_entries = subparsers.add_parser("list-entries", help="use this command to list your time entries")
parser_list_entries.add_argument("--start-date",
help=f"start date filter in format `{SIMPLE_DATETIME_FORMAT_HUMAN}` "
f"(default: now - 10days; unset for default)")
parser_list_entries.add_argument("--end-date",
help=f"end date filter in format `{SIMPLE_DATETIME_FORMAT_HUMAN}` "
f"(default: now; unset for default)")
parser_list_entries.add_argument("--items", help="max items to request per page", default=20, type=int)
# subparser command: `punch`
parser_punch = subparsers.add_parser("punch",
help="use this command to punch in, punch out, or create break entries")
parser_punch.add_argument("-t",
help=f"type of time entry; this can be {', '.join(PUNCH_COMMANDS)}",
default="punch_in",
choices=PUNCH_COMMANDS)
parser_punch.add_argument("-s", help=f"timestamp in format `{SIMPLE_DATETIME_FORMAT_HUMAN}` or `now`",
default="now")
# subparser command: `smart-punch`
parser_smart_punch = subparsers.add_parser("smart-punch",
help="use this command to auto punch in, punch out and create break "
"entries; this command tries to detect your last action and will "
"create entries in to following order: "
"punch_in -> break_start -> break_end -> punch_out")
parser_smart_punch.add_argument("-s", help=f"timestamp in format `{SIMPLE_DATETIME_FORMAT_HUMAN}` or `now`",
default="now")
args = parser.parse_args()
if args.v:
logger.setLevel(logging.DEBUG)
config = configparser.ConfigParser()
config.read(args.c)
ma = MobatimeApi(baseurl=config["general"]["baseurl"], user=args.u, password=args.p, ask_for_password=True,
save_session=args.save_cookies)
tb = TimeBot(mobatime_api=ma)
if args.subparser_name == "punch":
if args.s == "now":
punch_datetime = datetime.datetime.now()
else:
punch_datetime = datetime.datetime.strptime(args.s, SIMPLE_DATETIME_FORMAT)
logger.info("running `{}` with date `{}` and time `{}`".format(
args.t,
punch_datetime.strftime(SIMPLE_DATE_FORMAT),
punch_datetime.strftime(SIMPLE_TIME_FORMAT),
))
getattr(tb, args.t)(punch_datetime)
elif args.subparser_name == "smart-punch":
if args.s == "now":
punch_datetime = datetime.datetime.now()
else:
punch_datetime = datetime.datetime.strptime(args.s, SIMPLE_DATETIME_FORMAT)
tb.smart_punch(punch_datetime)
elif args.subparser_name == "list-entries":
end_date = None
if args.end_date:
end_date = datetime.datetime.strptime(args.end_date, SIMPLE_DATETIME_FORMAT)
start_date = None
if args.start_date:
start_date = datetime.datetime.strptime(args.start_date, SIMPLE_DATETIME_FORMAT)
data = tb.mobatime_api.get_entries(entries=args.items, start_date=start_date, end_date=end_date)
data.reverse()
for i in data:
print("Entry: {} - DateTime: {} - Note: {}".format(i["entryName"], i["dateTime"], i["note"]))
elif args.subparser_name == "status":
tracking_data = tb.mobatime_api.get_tracking_data()
account_info = tb.mobatime_api.get_account_information()
print("Tracking Info:")
print(f" Current State: {tracking_data['actualState']}")
print(f" Last Booking: {tracking_data['lastBooking']}")
print(f" Last Booking Date: {tracking_data['lastBookingDate']}")
print("Account Infos:")
for i in account_info:
print(f" {i['accountName']}: {i['value']}")
else:
logger.error("Noting done... dunno what you want!")
sys.exit(1)
sys.exit(0)
Loading…
Cancel
Save