def _run(self): chunks = [] while self._box is not None: try: packet = self._box.get(timeout=self._timeout) chunks.append(packet) except Empty: hex_id = binascii.hexlify(self.from_id) LOGGER.warning("{:5} {}@{}:{} ({}) timeout".format( '<-//-', hex_id[:8], self.ep[0], self.ep[1], PACKET_TYPES.get(self._packet_type))) # timeout self._box = None return None except: # die self._box = None raise try: if self._callback(chunks): hex_id = binascii.hexlify(self.from_id) LOGGER.info("{:5} {}@{}:{} ({}) ok".format( '<----', hex_id[:8], self.ep[0], self.ep[1], PACKET_TYPES.get(self._packet_type))) # job done self._box = None return chunks except: # die self._box = None raise
def ping(self, node, callback=None): """ send a ping request to the given node and return instantly invoke callback while reply arrives """ ping = PingNode(self.endpoint, node.endpoint, time.time() + K_EXPIRATION) message = self.wrap_packet(ping) msg_hash = message[:32] def reply_call(chunks): if chunks.pop().echo == msg_hash: if callback is not None: callback() return True ep = (node.endpoint.address.exploded, node.endpoint.udpPort) pending = self.add_pending(Pending(node, Pong.packet_type, reply_call)) self.sock.sendto(message, ep) LOGGER.info("{:5} {}@{}:{} (Ping)".format( '---->', binascii.hexlify(node.node_id)[:8], ep[0], ep[1])) return pending
def receive_find_neighbors(self, addr, pubkey, fn): remote_id = keccak256(pubkey) if time.time() - self.last_pong_received.get(remote_id, 0) > K_BOND_EXPIRATION: # lost origin or origin is off return target_id = keccak256(fn.target) closest = self.table.closest(target_id, BUCKET_SIZE) # sent neighbours in chunks ns = Neighbors([], time.time() + K_EXPIRATION) sent = False node_to = Node(EndPoint(addr[0], addr[1], addr[1]), pubkey) for c in closest: ns.nodes.append(c) if len(ns.nodes) == K_MAX_NEIGHBORS: self.send_sock(ns, node_to) LOGGER.info("{:5} {}@{}:{} {}".format( '---->', binascii.hexlify(node_to.node_id)[:8], addr[0], addr[1], ns)) ns.nodes = [] sent = True if len(ns.nodes) > 0 or not sent: self.send_sock(ns, node_to) LOGGER.info("{:5} {}@{}:{} {}".format( '---->', binascii.hexlify(node_to.node_id)[:8], addr[0], addr[1], ns))
def listen(self): LOGGER.info("{:5} listening...".format('')) while True: ready = select([self.sock], [], [], 1.0) if ready[0]: data, addr = self.sock.recvfrom(2048) # non-block data reading gevent.spawn(self.receive, data, addr)
def worked_day(self, day=date.today(), json_settings=DEFAULT_FACTORIAL_SETTINGS): """Mark today as worked day :param day: date to save the worked day, by default is today :param json_settings: string config filename """ with open(json_settings, 'r') as file: settings = json.load(file) work_settings_block = settings.get('work', {}) start_work = work_settings_block.get('start', '') end_work = work_settings_block.get('end', '') work_minutes_variation = work_settings_block.get( 'minutes_variation', 0) breaks = work_settings_block.get('breaks', []) already_work = self.get_day(year=day.year, month=day.month, day=day.day) if already_work: if work_settings_block.get('resave'): for worked_period in already_work: self.delete_worked_period(worked_period.get('id')) else: LOGGER.info('Day already sign') return add_worked_period_kwargs = { 'year': day.year, 'month': day.month, 'day': day.day, # Dynamic over loop fields 'start_hour': 0, 'start_minute': 0, 'end_hour': 0, 'end_minute': 0 } worked_periods = self.generate_worked_periods(start_work, end_work, work_minutes_variation, breaks) for worked_period in worked_periods: start_hour = worked_period.get('start_hour') start_minute = worked_period.get('start_minute') end_hour = worked_period.get('end_hour') end_minute = worked_period.get('end_minute') add_worked_period_kwargs.update({ 'start_hour': start_hour, 'start_minute': start_minute, 'end_hour': end_hour, 'end_minute': end_minute, }) if self.add_worked_period(**add_worked_period_kwargs): LOGGER.info( 'Saved worked period for the day {0:s} between {1:02d}:{2:02d} - {3:02d}:{4:02d}' .format(day.isoformat(), start_hour, start_minute, end_hour, end_minute))
def __run_worker(self): LOGGER.info("[WORKER] Launched!") while (data := self.__queue.get()) != "kill": try: MessageReader.load(data) LOGGER.info(f"[WORKER] [{data}] successfully processed!") except Exception as exp: LOGGER.error("[WORKER] Data processing operation failed!") LOGGER.error(f"[WORKER] {exp}")
def load(cls, data: str): LOGGER.info("[MSG-01] Parsing message...") card_str, _comma, rdata = data.partition(",") card_id = int(card_str) cp_cnt_str, _comma, rrdata = rdata.partition(",") cp_count = int(cp_cnt_str) val_list = rrdata.split(",") cp_list = val_list[0:cp_count] res_list = val_list[cp_count:] return cls(card_id, cp_list, res_list)
def load(data: str) -> None: fdata, _comma, check_sum = data.strip().partition("*") expected_sum = hex(int(f"0x{check_sum}", 16)) actual_sum = MessageReader.check_sum(fdata) if expected_sum == actual_sum: LOGGER.info("[READER] CheckSum Succeed!") msg_type, _comma, rdata = fdata.partition(",") return getattr(sys.modules[__name__], f"Message_{msg_type}").load(rdata) else: LOGGER.error( f"[READER] CheckSum Failed! Actual: {actual_sum}, Expected: {expected_sum}, Msg: {fdata}" )
def logout(self): """Logout invalidating that session, invalidating the cookie _factorial_session :return: bool """ response = self.session.delete(url=self.SESSION_URL) logout_correcty = response.status_code == http_client.NO_CONTENT LOGGER.info('Logout successfully {}'.format(logout_correcty)) self.session = requests.Session() path_file = os.path.join(self.SESSIONS_FOLDER, self.cookie_file) if os.path.exists(path_file): os.remove(path_file) logging.info('Logout: Removed cookies file') self.mates.clear() self.current_user = {} return logout_correcty
def add_worked_period(self, year, month, day, start_hour, start_minute, end_hour, end_minute): """Add the period as worked Example to create a worked period for the day 2019-07-31 from 7:30 to 15:30 - year 2019 - month 7 - day 31 - start_hour 7 - start_minute 30 - end_hour 15 - end_minute 30 :param year: integer :param month: integer :param day: integer :param start_hour: integer :param start_minute: integer :param end_hour: integer :param end_minute: integer :return bool: correctly saved """ # Check if are vacations calendar = self.get_calendar(year=year, month=month, is_leave=True) formatted_date = f'{year:04d}-{month:02d}-{day:02d}' for calendar_day in calendar: if calendar_day.get('date') == formatted_date: LOGGER.info( f"Can't sign today {formatted_date}, because are vacations" ) return False period = self.get_period(year=year, month=month) current_period = period[0] period_id = current_period['id'] payload = { 'clock_in': f'{start_hour}:{start_minute}', 'clock_out': f'{end_hour}:{end_minute}', 'day': day, 'period_id': period_id } response = self.session.post(self.SHIFT_URL, data=payload) self.check_status_code(response.status_code, http_client.CREATED) return True
def __init__(self, email, password, cookie_file=None): """Factorial client to automatically sign up the work :param email: (required) string, email to login on Factorial :param password: (required) string, password to login on Factorial :param cookie_file: (optional) string, file to save the cookies """ self.email = email self.password = password self.current_user = {} self.mates = [] self.session = requests.Session() # Be able to save the cookies on a file specified, or save each user on a different email for multi account self.cookie_file = cookie_file or hashlib.sha512( email.encode('utf-8')).hexdigest() cookie_path = os.path.join(self.SESSIONS_FOLDER, self.cookie_file) if os.path.exists(cookie_path): with open(cookie_path, "rb") as file: # TODO: Watch out the expiration of the cookie LOGGER.info('Getting the session from cookies files') self.session.cookies.update(pickle.load(file))
def find_neighbors(self, node, target_key): """ send a find neighbours request to the given node and waits until the node has sent up to k neighbours """ node_id = node.node_id if time.time() - self.last_ping_received.get(node_id, 0) > K_BOND_EXPIRATION: send_ping = self.ping(node) receive_ping = self.add_pending( Pending(node, PingNode.packet_type, lambda _: True)) # wait until endpoint proof is finished gevent.joinall([send_ping, receive_ping]) fn = FindNeighbors(target_key, time.time() + K_EXPIRATION) def reply_call(chunks): num_received = 0 for neighbors in chunks: num_received += len(neighbors.nodes) if num_received >= BUCKET_SIZE: return True pending = self.add_pending( Pending(node, Neighbors.packet_type, reply_call, timeout=2)) self.send_sock(fn, node) ep = (node.endpoint.address.exploded, node.endpoint.udpPort) LOGGER.info("{:5} {}@{}:{} (FN {})".format( '---->', binascii.hexlify(node.node_id)[:8], ep[0], ep[1], binascii.hexlify(keccak256(fn.target))[:8])) # block to wait for neighbours ret = pending.get() if ret: neighbor_nodes = [] for chunk in ret: for n in chunk.nodes: neighbor_nodes.append(n) return neighbor_nodes
def login(self): """Login on the factorial web :return: boolean if is logged in """ try: self.load_user_data() # Try to load the user info using the cookie, if can't login again using the username and password LOGGER.info('Already logged in, re-login is not needed') return True except UserNotLoggedIn: payload = { 'utf8': '✓', 'authenticity_token': self.generate_new_token(), 'user[email]': self.email, 'user[password]': self.password, 'user[remember_me]': "0", 'commit': 'Iniciar sesión' } response = self.session.post(url=self.SESSION_URL, data=payload) loggedin = response.status_code == http_client.CREATED if loggedin: LOGGER.info('Login successfully') # Load user data self.load_user_data() # Save the cookies if is logged in if not os.path.exists(self.SESSIONS_FOLDER): os.mkdir(self.SESSIONS_FOLDER) with open(os.path.join(self.SESSIONS_FOLDER, self.cookie_file), "wb") as file: pickle.dump(self.session.cookies, file) LOGGER.info('Sessions saved') return loggedin
def receive_ping(self, addr, pubkey, ping, msg_hash): remote_id = keccak256(pubkey) endpoint_to = EndPoint(addr[0], ping.endpoint_from.udpPort, ping.endpoint_from.tcpPort) pong = Pong(endpoint_to, msg_hash, time.time() + K_EXPIRATION) node_to = Node(pong.to, pubkey) # sending Pong response self.send_sock(pong, node_to) LOGGER.info("{:5} {}@{}:{} (Pong)".format( '---->', binascii.hexlify(node_to.node_id)[:8], addr[0], ping.endpoint_from.udpPort)) self.handle_reply(addr, pubkey, PingNode.packet_type, ping) node = Node(endpoint_to, pubkey) if time.time() - self.last_pong_received.get(remote_id, 0) > K_BOND_EXPIRATION: self.ping(node, lambda: self.add_table(node)) else: self.add_table(node) self.last_ping_received[remote_id] = time.time()
def load_employees(self): """Load employees info Example: [ { 'access_id' 'birthday_on' 'hired_on' 'job_title' 'id', 'manager_id' 'supervised_by_current' 'terminated_on' 'is_terminating' 'timeoff_policy_id' 'timeoff_manager_id' 'timeoff_supervised_by_current' 'location_id' 'employee_group_id' 'payroll_hiring_id' 'is_eligible_for_payroll' } ] """ LOGGER.info("Loading employees") employee_response = self.session.get(self.EMPLOYEE_URL) self.check_status_code(employee_response.status_code, http_client.OK) employee_json = employee_response.json() for employee in employee_json: # Update the user info that match the self.mates[n].id with employee.access_id for mate in self.mates: if mate.get('id') == employee.get('access_id'): mate.update(employee) if self.current_user.get('id') == employee.get('access_id'): self.current_user.update(employee)
def __run_listener(self): LOGGER.info("[LISTENER] Launched!") self.com_port = COMPort.auto_detect() self.__stop_cond = bool(self.com_port) self.__create_connection() while self.__stop_cond: data = self.__conn.readline() if len(data): data = data.decode("utf-8").strip() self.__queue.put(data) LOGGER.info(f"[LISTENER] Data: [{data}] recieved!") time.sleep(0.5) LOGGER.info("[LISTENER] Killed!") self.__queue.put("kill")
class COMPort(metaclass=Singleton): @staticmethod def auto_detect(port="CP210") -> str: return "COM2" com_list = COMPort.list_all() com_dict = next( (com_dict for com_dict in com_list if port in str(com_dict.values())), None) com_port = com_dict["name"] if com_dict else None return com_port @staticmethod def list_all() -> list: ports = serial.tools.list_ports.comports() port_list = [port.__dict__ for port in sorted(ports)] return port_list def __init__(self): self.__com_port = None self.__stop_cond = False self.__listener = None self.__worker = None self.__queue = None self.__conn = None def __create_connection(self): if self.com_port: self.__conn = Serial(self.com_port, 38400, timeout=0) @property def com_port(self) -> str: return self.__com_port @com_port.setter def com_port(self, val: str): self.__com_port = val def is_alive(self) -> bool: return bool(self.__listener and self.__listener.is_alive()) def stop(self): self.__stop_cond = False self.__com_port = None def start(self): if not (self.__listener and self.__listener.is_alive()): self.__queue = Queue() self.__listener = Thread( target=self.__run_listener, name="com-listener-thread", daemon=True) self.__listener.start() self.__worker = Thread( target=self.__run_worker, name="com-worker-thread", daemon=True) self.__worker.start() def __run_listener(self): LOGGER.info("[LISTENER] Launched!") self.com_port = COMPort.auto_detect() self.__stop_cond = bool(self.com_port) self.__create_connection() while self.__stop_cond: data = self.__conn.readline() if len(data): data = data.decode("utf-8").strip() self.__queue.put(data) LOGGER.info(f"[LISTENER] Data: [{data}] recieved!") time.sleep(0.5) LOGGER.info("[LISTENER] Killed!") self.__queue.put("kill") def __run_worker(self): LOGGER.info("[WORKER] Launched!") while (data := self.__queue.get()) != "kill": try: MessageReader.load(data) LOGGER.info(f"[WORKER] [{data}] successfully processed!") except Exception as exp: LOGGER.error("[WORKER] Data processing operation failed!") LOGGER.error(f"[WORKER] {exp}") LOGGER.info("[WORKER] Killed\n")