|
|
@ -16,9 +16,9 @@ logger = logging.getLogger() |
|
|
|
logging.basicConfig(level=logging.INFO) |
|
|
|
logging.basicConfig(level=logging.INFO) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TimeBot: |
|
|
|
class MobatimeApi: |
|
|
|
def __init__(self, baseurl: str, user: str, password: str = None, ask_for_password: bool = False, |
|
|
|
def __init__(self, baseurl: str, user: str, password: str = None, ask_for_password: bool = False, |
|
|
|
save_session: bool = False): |
|
|
|
save_session: bool = False, cookie_store: str = ".kekse"): |
|
|
|
self.logger = logging.getLogger(self.__class__.__name__) |
|
|
|
self.logger = logging.getLogger(self.__class__.__name__) |
|
|
|
self.baseurl = self._sanitize_baseurl(baseurl) |
|
|
|
self.baseurl = self._sanitize_baseurl(baseurl) |
|
|
|
self.user = user |
|
|
|
self.user = user |
|
|
@ -26,6 +26,7 @@ class TimeBot: |
|
|
|
self._ask_for_password = ask_for_password |
|
|
|
self._ask_for_password = ask_for_password |
|
|
|
self._save_session = save_session |
|
|
|
self._save_session = save_session |
|
|
|
self._session = None |
|
|
|
self._session = None |
|
|
|
|
|
|
|
self._cookie_store = cookie_store |
|
|
|
self._current_user = None |
|
|
|
self._current_user = None |
|
|
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
@staticmethod |
|
|
@ -94,7 +95,7 @@ class TimeBot: |
|
|
|
:param requests.Session session: the requests session to extract the cookies from |
|
|
|
:param requests.Session session: the requests session to extract the cookies from |
|
|
|
""" |
|
|
|
""" |
|
|
|
if self._save_session: |
|
|
|
if self._save_session: |
|
|
|
with open(".kekse", "wb") as f: |
|
|
|
with open(self._cookie_store, "wb") as f: |
|
|
|
self.logger.debug("pickling session cookies") |
|
|
|
self.logger.debug("pickling session cookies") |
|
|
|
pickle.dump(requests.utils.dict_from_cookiejar(session.cookies), f) |
|
|
|
pickle.dump(requests.utils.dict_from_cookiejar(session.cookies), f) |
|
|
|
|
|
|
|
|
|
|
@ -105,34 +106,30 @@ class TimeBot: |
|
|
|
:param requests.Session session: the requests session which will be updated with the loaded cookies |
|
|
|
:param requests.Session session: the requests session which will be updated with the loaded cookies |
|
|
|
""" |
|
|
|
""" |
|
|
|
if self._save_session: |
|
|
|
if self._save_session: |
|
|
|
with open(".kekse", "rb") as f: |
|
|
|
with open(self._cookie_store, "rb") as f: |
|
|
|
self.logger.debug("loading pickled cookies") |
|
|
|
self.logger.debug("loading pickled cookies") |
|
|
|
session.cookies.update(requests.utils.cookiejar_from_dict(pickle.load(f))) |
|
|
|
session.cookies.update(requests.utils.cookiejar_from_dict(pickle.load(f))) |
|
|
|
|
|
|
|
|
|
|
|
def add_entry(self, punch_datetime: datetime.datetime, entry_code: int, note: str = None) -> requests.Response: |
|
|
|
@property |
|
|
|
|
|
|
|
def current_user(self): |
|
|
|
""" |
|
|
|
""" |
|
|
|
Add mobatime entry. |
|
|
|
Returns all user information for the current user. |
|
|
|
|
|
|
|
|
|
|
|
:param datetime.datetime punch_datetime: datetim object |
|
|
|
:return: all user information as dict |
|
|
|
:param int entry_code: entry type code |
|
|
|
|
|
|
|
:param str note: free text note added to the Mobatime entry |
|
|
|
|
|
|
|
:return: requests response object |
|
|
|
|
|
|
|
""" |
|
|
|
""" |
|
|
|
punch_date = punch_datetime.strftime(SIMPLE_DATE_FORMAT) |
|
|
|
if self._current_user is None: |
|
|
|
punch_time = punch_datetime.strftime(SIMPLE_TIME_FORMAT) |
|
|
|
self._current_user = self.get_user_profile_for_current_user() |
|
|
|
entry_data = { |
|
|
|
return self._current_user |
|
|
|
"periode0Date": punch_date, |
|
|
|
|
|
|
|
"periode0Time": punch_time, |
|
|
|
def get_current_user_id(self): |
|
|
|
"selectedEntryCode": entry_code, |
|
|
|
""" |
|
|
|
"selectedPeriodType": 0, |
|
|
|
Get the current users id. |
|
|
|
"employeeId": self.get_current_user_id(), |
|
|
|
|
|
|
|
} |
|
|
|
:return: user id |
|
|
|
if note: |
|
|
|
""" |
|
|
|
entry_data["note"] = note |
|
|
|
return self.current_user["employee"]["id"] |
|
|
|
request = self.session.post(self.baseurl + "Entry/SaveEntry", data=entry_data) |
|
|
|
|
|
|
|
return request |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def list_employees(self): |
|
|
|
def get_employees(self): |
|
|
|
""" |
|
|
|
""" |
|
|
|
List all employees which are obviously in the same team as you. |
|
|
|
List all employees which are obviously in the same team as you. |
|
|
|
:return: list of employees |
|
|
|
:return: list of employees |
|
|
@ -141,7 +138,7 @@ class TimeBot: |
|
|
|
request.raise_for_status() |
|
|
|
request.raise_for_status() |
|
|
|
return request.json() |
|
|
|
return request.json() |
|
|
|
|
|
|
|
|
|
|
|
def _get_user_profile_for_current_user(self): |
|
|
|
def get_user_profile_for_current_user(self): |
|
|
|
""" |
|
|
|
""" |
|
|
|
Returns all user information for the current user. |
|
|
|
Returns all user information for the current user. |
|
|
|
|
|
|
|
|
|
|
@ -151,24 +148,28 @@ class TimeBot: |
|
|
|
request.raise_for_status() |
|
|
|
request.raise_for_status() |
|
|
|
return request.json() |
|
|
|
return request.json() |
|
|
|
|
|
|
|
|
|
|
|
@property |
|
|
|
def save_entry(self, punch_datetime: datetime.datetime, entry_code: int, note: str = None) -> requests.Response: |
|
|
|
def current_user(self): |
|
|
|
|
|
|
|
""" |
|
|
|
""" |
|
|
|
Returns all user information for the current user. |
|
|
|
Add mobatime entry. |
|
|
|
|
|
|
|
|
|
|
|
:return: all user information as dict |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
if self._current_user is None: |
|
|
|
|
|
|
|
self._current_user = self._get_user_profile_for_current_user() |
|
|
|
|
|
|
|
return self._current_user |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_current_user_id(self): |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
Get the current users id. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
:return: user id |
|
|
|
:param datetime.datetime punch_datetime: datetim object |
|
|
|
|
|
|
|
:param int entry_code: entry type code |
|
|
|
|
|
|
|
:param str note: free text note added to the Mobatime entry |
|
|
|
|
|
|
|
:return: requests response object |
|
|
|
""" |
|
|
|
""" |
|
|
|
return self.current_user["employee"]["id"] |
|
|
|
punch_date = punch_datetime.strftime(SIMPLE_DATE_FORMAT) |
|
|
|
|
|
|
|
punch_time = punch_datetime.strftime(SIMPLE_TIME_FORMAT) |
|
|
|
|
|
|
|
entry_data = { |
|
|
|
|
|
|
|
"periode0Date": punch_date, |
|
|
|
|
|
|
|
"periode0Time": punch_time, |
|
|
|
|
|
|
|
"selectedEntryCode": entry_code, |
|
|
|
|
|
|
|
"selectedPeriodType": 0, |
|
|
|
|
|
|
|
"employeeId": self.get_current_user_id(), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if note: |
|
|
|
|
|
|
|
entry_data["note"] = note |
|
|
|
|
|
|
|
request = self.session.post(self.baseurl + "Entry/SaveEntry", data=entry_data) |
|
|
|
|
|
|
|
return request |
|
|
|
|
|
|
|
|
|
|
|
def get_entries(self, entries: int = 10, |
|
|
|
def get_entries(self, entries: int = 10, |
|
|
|
start_date: datetime.datetime = None, |
|
|
|
start_date: datetime.datetime = None, |
|
|
@ -235,33 +236,68 @@ class TimeBot: |
|
|
|
request.raise_for_status() |
|
|
|
request.raise_for_status() |
|
|
|
return request.json() |
|
|
|
return request.json() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TimeBot: |
|
|
|
|
|
|
|
def __init__(self, mobatime_api: MobatimeApi): |
|
|
|
|
|
|
|
self.logger = logging.getLogger(self.__class__.__name__) |
|
|
|
|
|
|
|
self.mobatime_api = mobatime_api |
|
|
|
|
|
|
|
|
|
|
|
def punch_in(self, punch_datetime: datetime.datetime): |
|
|
|
def punch_in(self, punch_datetime: datetime.datetime): |
|
|
|
""" |
|
|
|
""" |
|
|
|
:param datetime.datetime punch_datetime: datetime object |
|
|
|
:param datetime.datetime punch_datetime: datetime object |
|
|
|
:raises: on status code != 2xx |
|
|
|
:raises: on status code != 2xx |
|
|
|
""" |
|
|
|
""" |
|
|
|
self.add_entry(punch_datetime, COMING_ENTRY_CODE_ID, note="da").raise_for_status() |
|
|
|
self.mobatime_api.save_entry(punch_datetime, COMING_ENTRY_CODE_ID, note="da").raise_for_status() |
|
|
|
|
|
|
|
|
|
|
|
def punch_out(self, punch_datetime: datetime.datetime): |
|
|
|
def punch_out(self, punch_datetime: datetime.datetime): |
|
|
|
""" |
|
|
|
""" |
|
|
|
:param datetime.datetime punch_datetime: datetime object |
|
|
|
:param datetime.datetime punch_datetime: datetime object |
|
|
|
:raises: on status code != 2xx |
|
|
|
:raises: on status code != 2xx |
|
|
|
""" |
|
|
|
""" |
|
|
|
self.add_entry(punch_datetime, LEAVING_ENTRY_CODE_ID, note="weg").raise_for_status() |
|
|
|
self.mobatime_api.save_entry(punch_datetime, LEAVING_ENTRY_CODE_ID, note="weg").raise_for_status() |
|
|
|
|
|
|
|
|
|
|
|
def break_start(self, punch_datetime: datetime.datetime): |
|
|
|
def break_start(self, punch_datetime: datetime.datetime): |
|
|
|
""" |
|
|
|
""" |
|
|
|
:param datetime.datetime punch_datetime: datetime object |
|
|
|
:param datetime.datetime punch_datetime: datetime object |
|
|
|
:raises: on status code != 2xx |
|
|
|
:raises: on status code != 2xx |
|
|
|
""" |
|
|
|
""" |
|
|
|
self.add_entry(punch_datetime, BREAK_START_ENTRY_CODE_ID, note="pause").raise_for_status() |
|
|
|
self.mobatime_api.save_entry(punch_datetime, BREAK_START_ENTRY_CODE_ID, note="pause").raise_for_status() |
|
|
|
|
|
|
|
|
|
|
|
def break_end(self, punch_datetime: datetime.datetime): |
|
|
|
def break_end(self, punch_datetime: datetime.datetime): |
|
|
|
""" |
|
|
|
""" |
|
|
|
:param datetime.datetime punch_datetime: datetime object |
|
|
|
:param datetime.datetime punch_datetime: datetime object |
|
|
|
:raises: on status code != 2xx |
|
|
|
:raises: on status code != 2xx |
|
|
|
""" |
|
|
|
""" |
|
|
|
self.add_entry(punch_datetime, BREAK_END_ENTRY_CODE_ID, note="pause ende").raise_for_status() |
|
|
|
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)) |
|
|
|
|
|
|
|
method = None |
|
|
|
|
|
|
|
if not last_punch: |
|
|
|
|
|
|
|
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") |
|
|
|
|
|
|
|
method = "break_start" |
|
|
|
|
|
|
|
elif last_punch[0]["entryNumber"] == BREAK_START_ENTRY_CODE_ID: |
|
|
|
|
|
|
|
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") |
|
|
|
|
|
|
|
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") |
|
|
|
|
|
|
|
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}") |
|
|
|
|
|
|
|
exit(1) |
|
|
|
|
|
|
|
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__': |
|
|
|
if __name__ == '__main__': |
|
|
@ -313,8 +349,9 @@ if __name__ == '__main__': |
|
|
|
config = configparser.ConfigParser() |
|
|
|
config = configparser.ConfigParser() |
|
|
|
config.read(args.c) |
|
|
|
config.read(args.c) |
|
|
|
|
|
|
|
|
|
|
|
tb = TimeBot(baseurl=config["general"]["baseurl"], user=args.u, password=args.p, ask_for_password=True, |
|
|
|
ma = MobatimeApi(baseurl=config["general"]["baseurl"], user=args.u, password=args.p, ask_for_password=True, |
|
|
|
save_session=args.save_cookies) |
|
|
|
save_session=args.save_cookies) |
|
|
|
|
|
|
|
tb = TimeBot(mobatime_api=ma) |
|
|
|
if args.subparser_name == "punch": |
|
|
|
if args.subparser_name == "punch": |
|
|
|
if args.s == "now": |
|
|
|
if args.s == "now": |
|
|
|
punch_datetime = datetime.datetime.now() |
|
|
|
punch_datetime = datetime.datetime.now() |
|
|
@ -331,33 +368,7 @@ if __name__ == '__main__': |
|
|
|
punch_datetime = datetime.datetime.now() |
|
|
|
punch_datetime = datetime.datetime.now() |
|
|
|
else: |
|
|
|
else: |
|
|
|
punch_datetime = datetime.datetime.strptime(args.s, SIMPLE_DATETIME_FORMAT) |
|
|
|
punch_datetime = datetime.datetime.strptime(args.s, SIMPLE_DATETIME_FORMAT) |
|
|
|
last_punch = tb.get_entries(1, punch_datetime.replace(hour=0, minute=0, second=0, microsecond=0)) |
|
|
|
tb.smart_punch(punch_datetime) |
|
|
|
method = None |
|
|
|
|
|
|
|
if not last_punch: |
|
|
|
|
|
|
|
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") |
|
|
|
|
|
|
|
method = "break_start" |
|
|
|
|
|
|
|
elif last_punch[0]["entryNumber"] == BREAK_START_ENTRY_CODE_ID: |
|
|
|
|
|
|
|
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") |
|
|
|
|
|
|
|
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") |
|
|
|
|
|
|
|
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}") |
|
|
|
|
|
|
|
exit(1) |
|
|
|
|
|
|
|
logger.info("running `{}` with date `{}` and time `{}`".format( |
|
|
|
|
|
|
|
method, |
|
|
|
|
|
|
|
punch_datetime.strftime(SIMPLE_DATE_FORMAT), |
|
|
|
|
|
|
|
punch_datetime.strftime(SIMPLE_TIME_FORMAT), |
|
|
|
|
|
|
|
)) |
|
|
|
|
|
|
|
getattr(tb, method)(punch_datetime) |
|
|
|
|
|
|
|
elif args.subparser_name == "list-entries": |
|
|
|
elif args.subparser_name == "list-entries": |
|
|
|
end_date = None |
|
|
|
end_date = None |
|
|
|
if args.end_date: |
|
|
|
if args.end_date: |
|
|
@ -365,13 +376,13 @@ if __name__ == '__main__': |
|
|
|
start_date = None |
|
|
|
start_date = None |
|
|
|
if args.start_date: |
|
|
|
if args.start_date: |
|
|
|
start_date = datetime.datetime.strptime(args.start_date, SIMPLE_DATETIME_FORMAT) |
|
|
|
start_date = datetime.datetime.strptime(args.start_date, SIMPLE_DATETIME_FORMAT) |
|
|
|
data = tb.get_entries(entries=args.items, start_date=start_date, end_date=end_date) |
|
|
|
data = tb.mobatime_api.get_entries(entries=args.items, start_date=start_date, end_date=end_date) |
|
|
|
data.reverse() |
|
|
|
data.reverse() |
|
|
|
for i in data: |
|
|
|
for i in data: |
|
|
|
print("Entry: {} - DateTime: {} - Note: {}".format(i["entryName"], i["dateTime"], i["note"])) |
|
|
|
print("Entry: {} - DateTime: {} - Note: {}".format(i["entryName"], i["dateTime"], i["note"])) |
|
|
|
elif args.subparser_name == "status": |
|
|
|
elif args.subparser_name == "status": |
|
|
|
tracking_data = tb.get_tracking_data() |
|
|
|
tracking_data = tb.mobatime_api.get_tracking_data() |
|
|
|
account_info = tb.get_account_information() |
|
|
|
account_info = tb.mobatime_api.get_account_information() |
|
|
|
print("Tracking Info:") |
|
|
|
print("Tracking Info:") |
|
|
|
print(f" Current State: {tracking_data['actualState']}") |
|
|
|
print(f" Current State: {tracking_data['actualState']}") |
|
|
|
print(f" Last Booking: {tracking_data['lastBooking']}") |
|
|
|
print(f" Last Booking: {tracking_data['lastBooking']}") |
|
|
|