def add_log(task_id: ObjectId, task_type: str, user: dict): task_cls = tasks_cls_for(task_type) task = task_cls().get(task_id) if task is None: raise errors.NotFound() request_json = request.get_json() task_cls.update_logs( task_id, worker_log=request_json.get("worker_log"), installer_log=request_json.get("installer_log"), uploader_log=request_json.get("uploader_log"), downloader_log=request_json.get("downloader_log"), wipe_log=request_json.get("wipe_log"), writer_log=request_json.get("writer_log"), ) # update ACK Acknowlegments.busy_update( username=user["username"], worker_type=task_type, slot=request.args.get("slot"), task_id=task_id, ) return jsonify({"_id": task_id})
def typed_collection(task_type: str, user: dict): if request.method == "GET": Acknowlegments.idle_update( username=user["username"], worker_type=task_type, slot=request.args.get("slot"), ) tasks = tasks_cls_for(task_type).find_availables( channel=user.get("channel")) return jsonify(tasks)
def sos(user: dict): request_json = request.get_json() try: request_json = request.get_json() if "type" not in request_json: raise ValidationError("missing worker type") except ValidationError as error: raise errors.BadRequest(str(error)) # update ACK aid, status_changed = Acknowlegments.sos_update( username=user["username"], worker_type=request_json.get("type"), slot=request.args.get("slot"), error=request_json.get("error"), ) print("aid", aid, "changed", status_changed) # only email operator once if status_changed: send_worker_sos_email(aid) return jsonify({"_id": aid})
def register_task(task_id: ObjectId, task_type: str, user: dict): task_cls = tasks_cls_for(task_type) task = task_cls().find({"_id": task_id, "status": task_cls.pending}) if task is None: raise errors.NotFound() task_cls.register(task_id, user) # update ACK Acknowlegments.busy_update( username=user["username"], worker_type=task_type, slot=request.args.get("slot"), task_id=task_id, ) return jsonify({"_id": task_id})
def send_worker_sos_email(ack_id): # client: image writing successful. ack = Acknowlegments.get(ack_id) context = {"ack": ack} subject = jinja_env.get_template("subject_worker_sos.txt").render(**context) content = jinja_env.get_template("operator_worker_sos.html").render(**context) send_email( to=Users().by_username(ack["username"])["email"], subject=subject, contents=content, )
def collection(user: dict): skip = request.args.get("skip", default=0, type=int) limit = request.args.get("limit", default=20, type=int) skip = 0 if skip < 0 else skip limit = 20 if limit <= 0 else limit cursor = Acknowlegments().find({}) workers = [user for user in cursor] return jsonify({"meta": {"skip": skip, "limit": limit}, "items": workers})
def collection(user: dict): skip = request.args.get("skip", default=0, type=int) limit = request.args.get("limit", default=20, type=int) skip = 0 if skip < 0 else skip limit = 20 if limit <= 0 else limit query = {} projection = None cursor = ( Acknowlegments() .find(query, projection) .sort([("$natural", pymongo.ASCENDING)]) .skip(skip) .limit(limit) ) count = Acknowlegments().count_documents(query) workers = [worker for worker in cursor] return jsonify( {"meta": {"skip": skip, "limit": limit, "count": count}, "items": workers} )
def calculate_load(user: dict): now = datetime.datetime.now() def is_connected(worker): """whether a worker is considered connected (ping 15mn ago)""" try: return (now - worker["on"]).total_seconds() <= 900 # 15mn except Exception: raise return False def get_remaining_minutes(task): """estimated remaining duration of task based on units and when/if started""" # physical card units are 10 times download ones but creator impact is identical units = task["units"] / 10 if task["units"] > 512 else task["units"] # small images are done in about an hour # larger one increase duration with size ; especially due to upload duration = max(int(units * 1.875), 90) if units >= 32 else 60 if task["status"] in ["pending", "received"]: # task has not started return duration # already started, remove spent time from received for event in task.get("statuses", []): if event["status"] == "received": passed = datetime.datetime.now() - event["on"] return duration - (passed.total_seconds() // 60) # couldn't find received ; returning full duration return duration # get number of online workers cursor = Acknowlegments().find({}, None).sort([("$natural", pymongo.ASCENDING)]) nb_connected_workers = len([ worker for worker in cursor if worker.get("worker_type") == "creator" and is_connected(worker) ]) # get list of pending task and calculate remaining durations for those tasks = [] for task in CreatorTasks().find( { "status": { "$in": ["pending", "received", "building", "built", "uploading"] } }, { "status": 1, "statuses": 1, "order": 1, "worker": 1 }, ): task["units"] = Orders().find_one({"_id": task["order"]}, {"units": 1})["units"] task["duration"] = get_remaining_minutes(task) tasks.append(task) nb_pending_taks = len(tasks) cumulative_duration = sum([task["duration"] for task in tasks]) try: remaining_minutes = cumulative_duration // nb_connected_workers except ZeroDivisionError: remaining_minutes = None # no worker estimated_completion = (now + datetime.timedelta(seconds=remaining_minutes * 60) if remaining_minutes else now) return jsonify({ "connected_workers": nb_connected_workers, "pending_tasks": nb_pending_taks, "cumulative_duration": cumulative_duration, "remaining_minutes": remaining_minutes, "estimated_completion": estimated_completion.isoformat(), })