def my_job_data(self): """dict: ``lists`` of ``Projects`` owned by the active user and organized by job number. Notes ----- These ``Projects`` are no longer linked with their corresponding drawing numbers. """ return JobIO.sort_project_data(self.my_projects)
def _close(self, msg): """Remove job files and send confirmation email. Parameters ---------- msg : list Document control message. """ if JobIO.clear_job_files(self._job_num): msg.append('Yes') send_email(self._to, [], '%s Completed' % self._job_num, '<br>'.join(msg), True)
def my_projects(self): """dict: A collection of ``Projects`` owned by the active user and organized by drawing number. """ my_projects = {} if self.my_name is None: my_projects existing_projects = JobIO.existing_projects() for project in existing_projects.keys(): if existing_projects[project].owner == self.my_name: my_projects[project] = existing_projects[project] return my_projects
def _validate_job_num(self): """Confirm that a job number is valid and does not already exist. Raises ------ JobNumberError ExistingJobError OSError """ if len(self._job_num) != 6: raise JobNumberError() elif JobIO.job_exists(self._job_num): raise ExistingJobError()
def process_complete_job_request(self, job_num, job=None, lock=None): """Process a request to remove job files from ``Nucleus``. Parameters ---------- job_num : str The 6-digit job number. job : Job or None A collection of work orders requesting completion. lock : GateKeeper or None Controls read and write access to `job` applicaton files. Returns ------- True If `job` files were removed from ``Nucleus``. """ self.app_data.users.log('initiating a complete job request for %s' % job_num) request = CompleteJobRequest( job_num, self.app_data.users.supervisor_email_addresses, job, lock) try: if not request.approved(): self.app_data.users.log( '%s complete job request was not approved' % job_num) return except (JobInUseError, IOError, EOFError, ProjectsFolderRootError, DestinationError) as error: self.app_data.users.log(error) ExceptionMessageBox(error).exec_() else: self.status.showMessage('Updating your jobs...') self.desk.refresh_home() # Log data to user file self.app_data.users.log('%s completed, due by %s' % (job_num, JobIO.job_due_date(job))) self.app_data.users.log('%s drawing count: %d' % (job_num, request.dwg_count)) for project in request.projects: self.app_data.users.log('project:%s,%s' % (request.projects[project].alias_num, request.projects[project].owner)) self.status.showMessage('%s closed successfully.' % job_num) return True
def _assign_job(self, job_num, df): """Create and update new job files. Parameters ---------- job_num : str df : DataFrame Contains information corresponding to `job_num` only. Raises ------ IOError JobNotFoundError JobInUseError EOFError """ JobIO.init_files(job_num, None) job, lock = JobIO.job_and_lock(job_num) for index, row in df.iterrows(): self._transform_row_into_project(row, job) JobIO.save(job_num, job) lock.unlock()
def my_jobs_at_a_glance(self): """dict: ``dicts`` comprised of due date information for the active user's projects organized by job number. Nested keys: 'expired' (past due), 'today' (due today), and 'approaching' (due within 2 days). Nested values (``int``): The number of ``Projects`` whose due dates fall within the key category. """ # LEAD was introduced to provide the drafting lead with a glance at # all department projects, not just his/her own. LEAD is still # classified with a user level of 'Technician' so that he or she can be # assigned projects, which is an option 'Supervisor' users do not have. LEAD = 'Jaye' if self.my_level == 'Supervisor' or self.my_name == LEAD: # Supervisors and leads are linked with all jobs. return JobIO.jobs_at_a_glance( JobIO.sort_project_data(JobIO.existing_projects())) elif self.my_level == 'Technician': return JobIO.jobs_at_a_glance(self.my_job_data)
def load_job(self, job_num): """Send a ``Job`` to `desk` and open for viewing. Parameters ---------- job_num : str The 6-digit job number. Raises ------ JobNotFoundError JobInUseError IOError EOFError """ job, lock = JobIO.job_and_lock(job_num) self.desk.open_job_folder(job, lock)
def _drop_active_jobs(self, df): """Remove rows that have been assigned. Parameters ---------- df : DataFrame Raises ------ EmptyModelError All rows in `df` were removed. """ assigned = JobIO.active_job_nums() assigned_indices = [ index for index, row in df.iterrows() if row['WC line alias'][:6] in assigned ] df.drop(assigned_indices, inplace=True) if len(df.index) == 0: raise EmptyModelError()
def approved(self): """Evaluate a new job request. Returns ------- True If job files were created successfully. Raises ------ JobNumberError ExistingJobError WorkspaceError OSError IOError """ self._validate_job_num() self.workspace = ProjectWorkspace.make(self._job_num, self._logger) if self.workspace is not None: return JobIO.init_files( self._job_num, self.workspace )
def status(selected_dwg_nums, projects, status, workspace, log): """Modify a project's status attribute. Parameters ---------- selected_dwg_nums : list Drawing numbers associated with the ``Projects`` that are requesting modification. projects : dict ``Projects`` organized by drawing number. status : str The new status. workspace : str The toplevel project workspace directory. log : callable Interface to user log file. Notes ----- When `status` signifies the completion of a work order, an attempt is made to send the corresponding non-controlled drawing PDF to the issued prints folder. This PDF should be created through Autodesk Inventor per the local iLogic code, otherwise the resulting PDF name may not match the required convention. In this case, this method will not find the PDF and the user will be responsible to completing this action. """ if status == WorkOrderConstants.STATUS_LIST[-1]: job_num = selected_dwg_nums[0][:6] dwgs_nums = JobIO.drawing_nums_from_list(selected_dwg_nums) target_count = len(dwgs_nums) actual_count = 0 moved_dwg_nums = [] # Get the destination for non-controlled drawing PDFs. try: dst = Extract.issued_prints_folder(job_num) except (DestinationError, ProjectsFolderRootError) as error: ExceptionMessageBox(error).exec_() return pdfs = ContextHandler.workspace_pdfs(workspace) for pdf in pdfs: if actual_count < target_count: # Get the filename and drawing number from pdf. # The '_' separator is driven by Inventor iLogic code. # Non-controlled PDF files must conform to this convention. filename = os.path.basename(pdf) target_dwg_num = filename.split('_')[0] if target_dwg_num in selected_dwg_nums: dst_path = os.path.join(dst, filename) shutil.move(pdf, dst_path) actual_count += 1 moved_dwg_nums.append(target_dwg_num) # Check for PDFs that should exist, but don't. missing_pdfs = [i for i in dwgs_nums if i not in moved_dwg_nums] if len(missing_pdfs) != 0: log('%s PDF not moved, there may be others' % missing_pdfs[0]) MissingPDFError.show(missing_pdfs) for p in selected_dwg_nums: projects[p].status = status
def refresh(self): """Update data model.""" self._data = JobIO.existing_projects()