def __init__(self, unit: str): """Object that represents path to unit's base folder Parameters --- Unit: string Examples ------- >>> uf = UnitFolder(unit='F301') >>> uf.p_unit '/Volumes/Public/Fort Hills/02. Equipment Files/1. 980E Trucks/F301 - A40017' """ # get unit's row from unit table, save to self attributes m = db.get_df_unit().loc[unit] f.copy_dict_attrs(m=m, target=self) modelpath = self.get_modelpath() # needs model and model_map unitpath = f'{unit} - {self.serial}' if not 'shovels' in self.minesite.lower(): p_unit = cf.p_drive / f'{self.equippath}/{modelpath}/{unitpath}' else: # shovels doesn't want modelpath. Could make this a list of exclusions or something p_unit = cf.p_drive / f'{self.equippath}/{unitpath}' p_dls = p_unit / 'Downloads' p_dls_year = p_dls / str(dt.now().year) f.set_self(vars())
def process_df(self, df): """Pivot dates for first of months, then merge unit delivery date/serial from db - will fail if most recent dates not in db """ df = df \ .assign(DateSMR=lambda x: x.DateSMR.dt.strftime('%Y-%m-%d')) \ .pivot(index='Unit', columns='DateSMR', values='SMR') \ .rename_axis('Unit', axis=1) \ .assign(Difference=lambda x: x.iloc[:, 1] - x.iloc[:, 0]) return db.get_df_unit(minesite=self.minesite) \ .set_index('Unit')[['Serial', 'DeliveryDate']] \ .merge(right=df, how='right', on='Unit') \ .reset_index()
def create_fc_manual(units: list, fc_number: str, _type: str, subject: str, release_date: dt, expiry_date: dt, **kw) -> None: """Create manual FC from input dialog""" df = db.get_df_unit() \ .pipe(lambda df: df[df.Unit.isin(units)])[['Model', 'Serial']] \ .reset_index() \ .assign( FCNumber=fc_number, Subject=subject, Classification=_type, StartDate=release_date, EndDate=expiry_date) import_fc(df=df, **kw)
def unit_from_path(p): """Regex find first occurance of unit in path - Needs minesite """ minesite = plm.minesite_from_path(p) units = db.get_df_unit() \ .pipe(lambda df: df[df.MineSite == minesite]) \ .Unit.unique().tolist() match = re.search(f'({"|".join(units)})', str(p)) if match: return match.groups()[0] else: log.warning(f'Couldn\'t find unit in path: {p}')
def read_fc_old(p): # Raw FC data comes from KA as an html page disguised as an xls # TODO: Drop ServiceLetterDate > same as StartDate # TODO: Drop CompletionDate > changed to DateCompleteKA, Drop ClaimNumber with open(p) as html: table = BeautifulSoup( html, features='lxml').findAll('table')[2] # FC data is in 3rd table cols = [hdr.text for hdr in table.find('thead').find_all('th')] data = [[col.text for col in row.findAll('td')] for row in table.findAll('tr')[1:]] dfu = db.get_df_unit() df = pd.DataFrame(data=data, columns=cols) \ .pipe(f.parse_datecols) \ .merge(right=dfu[['Model', 'Serial', 'Unit']], how='left') # Remove missing units # TODO: Return these to user somehow? df.Unit.replace('', pd.NA, inplace=True) df.dropna(subset=['Unit'], inplace=True) # Rename Cols df.columns = [ 'FCNumber', 'Distributor', 'Branch', 'Model', 'Serial', 'Safety', 'StartDate', 'EndDate', 'Subject', 'ClaimNumber', 'CompletionSMR', 'DateCompleteKA', 'Status', 'Hours', 'ServiceLetterDate', 'Classification', 'Unit' ] # noqa # Drop and reorder, Don't import: CompletionSMR, claimnumber, ServiceLetterDate cols = [ 'FCNumber', 'Model', 'Serial', 'Unit', 'StartDate', 'EndDate', 'DateCompleteKA', 'Subject', 'Classification', 'Hours', 'Distributor', 'Branch', 'Safety', 'Status' ] df = df[cols] df.FCNumber = df.FCNumber.str.strip() return df
def read_fc(p: Path) -> pd.DataFrame: """ Read FC csv from KA - Removes units not in db """ # Drop and reorder, Don't import: CompletionSMR, claimnumber, ServiceLetterDate cols = [ 'FCNumber', 'Model', 'Serial', 'Unit', 'StartDate', 'EndDate', 'DateCompleteKA', 'Subject', 'Classification', 'Branch', 'Status' ] dtypes = dict(Model='object', Serial='object', Unit='object') # NOTE only model colulmn has '\t' so far, but would be better to apply to all str cols dfu = db.get_df_unit() return f.read_csv_firstrow(p, dtype=dtypes, skipinitialspace=True) \ .rename(columns=cf.config['Headers']['FCImport']) \ .assign(Model=lambda x: x.Model.str.strip()) \ .merge(right=dfu[['Model', 'Serial', 'Unit']], how='left') \ .pipe(db.filter_database_units) \ .reset_index(drop=True)[cols]
def cb_changed(self, field_name: str) -> None: """Update units/models/model_base lists when minesite changed - TODO could clear filters when fields de-selected also Parameters ---------- index : int list index of current minesite, not really needed """ # minesite = self.fMineSite.val field = getattr(self, f'f{field_name}') if not field.cb.isChecked(): return val = field.val df = db.get_df_unit() dependent_cols = self.dependents[field_name] for col in dependent_cols: name = f'f{col}' if hasattr(self, name) and not col in self.existing_dependent_cols( field_name): # Evaluate each field which is changing based on the active filters it is dependent on conds = [] for dep_col in self.dependent_on(col): if self.field_active(dep_col): cond = df[dep_col] == self.fields[dep_col].val conds.append(cond) items = df[np.all(conds, axis=0)][col].pipe(f.clean_series) field_change = getattr(self, name) field_change.box.set_items(items)
def from_model(cls, e, **kw): """Create report from model e, with header data from event dict + unit info""" header_data = dbt.model_dict(e, include_none=True) header_data.update(db.get_df_unit().loc[e.Unit]) return cls(e=e, header_data=header_data, **kw)
def add_feature(self, name: str, table: str = None, cb_enabled: bool = False) -> None: """Add single feature to refresh menu""" parent, ms = self.parent, self.minesite name, title = name.lower(), name.replace('_', ' ').title() IPF = InputField add_input, add_refresh_button = self.add_input, self.add_refresh_button if table is None: table = self.tables.get(name, None) # this is ugly, would rather set up objects and only call when needed, but hard to initialize if name == 'all_open': add_refresh_button(name=title, func=parent.refresh_allopen) elif name == 'last_week': add_refresh_button( name=title, func=lambda: parent.refresh_lastweek(base=False)) elif name == 'last_month': add_refresh_button( name=title, func=lambda: parent.refresh_lastmonth(base=False)) # TODO remove eventually, temp for EmailList table elif name == 'minesite_like': title = 'MineSite' add_input(field=IPF(text=title, default=ms, like=True), items=cf.config[title], checkbox=True) elif name == 'minesite': lst = db.get_list_minesite() add_input(field=IPF(text='MineSite', default=ms, table=table), items=lst, checkbox=True, box_changed=lambda x: self.cb_changed('MineSite')) elif name == 'work_order': add_input(field=IPF(text=title), checkbox=True, cb_enabled=False) elif name == 'unit': df = db.get_df_unit() lst = df[df.MineSite == ms].Unit.pipe(f.clean_series) add_input(field=IPF(text=title), items=lst, checkbox=True, cb_enabled=False) elif 'model' in name: col = 'ModelBase' if 'model_base' in name else 'Model' df = db.get_df_unit() if not 'all' in name: df = df.query('MineSite == @ms') lst = df[col].pipe(f.clean_series) add_input(field=IPF(text=f.lower_cols(col)[0].replace('_', ' ').title(), table=table), items=lst, checkbox=True, cb_enabled=False, box_changed=lambda x: self.cb_changed(col)) elif name == 'type': add_input(field=IPF(text=title, col_db='Classification', table=table), items=['M', 'FAF', 'DO', 'FT'], checkbox=True, cb_enabled=False) elif name == 'fc_number': df = db.get_df_fc(default=True, minesite=ms) lst = f.clean_series(s=df['FC Number']) add_input(field=IPF(text='FC Number'), items=lst, checkbox=True, cb_enabled=False) elif name == 'fc_complete': add_input(field=IPF(text=title, col_db='Complete'), items=['False', 'True'], checkbox=True, cb_enabled=False) elif name == 'manual_closed': add_input( field=IPF(text=title, default='False', table=table), items=['False', 'True'], checkbox=True, tooltip= '"Manual Closed = True" means FCs that HAVE been closed/removed from the FC Summary table. \ This is used to manage which FCs are "active".') elif name == 'start_date': # TODO: only set up for eventlog table currently add_input(field=IPF(text=title, dtype='date', col_db=self.col_db_startdate), checkbox=True, cb_enabled=False) elif name == 'end_date': add_input(field=IPF(text=title, dtype='date', col_db=self.col_db_enddate, opr=op.le), checkbox=True, cb_enabled=False) elif name == 'component': df = db.get_df_component() lst = f.clean_series(df.Component) add_input(field=IPF(text=title, table=table), items=lst, checkbox=True, cb_enabled=False) elif name == 'component_oil': add_input(field=IPF(text='Component', col_db='component_id'), items=[], checkbox=True, cb_enabled=False) elif name == 'limit_component': title = 'Limit per Component' field = add_input(field=IPF(text=title, dtype='int', default=5), checkbox=True, cb_enabled=True) field.exclude_filter = True elif name == 'tsi_author': add_input(field=IPF(text='TSI Author', default=self.mainwindow.username, col_db='TSIAuthor'), checkbox=True, cb_enabled=False) elif name == 'tsi_number': add_input(field=IPF(text='TSI Number'), checkbox=True, cb_enabled=False) elif name == 'major_components': add_input(field=IPF(text=title, default='True', table=table, col_db='Major'), items=['True', 'False'], checkbox=True, cb_enabled=cb_enabled) elif name == 'title': add_input( field=IPF(text=title, col_db=title, like=True), checkbox=True, cb_enabled=False, tooltip= 'Use wildcards * to match results containing partial text.\nEg:\n\ - Steering* > return "Steering Pump" and "Steering Arm", but not "Change Steering Pump"\n\ - *MTA* > return "The MTA Broke" and "MTA Failure"') elif name == 'fc_subject': df = db.get_df_fc(default=False, minesite=ms) lst = f.clean_series(df.Subject) add_input(field=IPF(text='Subject', table='FCSummary', col_db='SubjectShort'), items=lst, checkbox=True, cb_enabled=False) elif name == 'user_group': u = self.mainwindow.u enabled = False if self.parent.title in ( 'Event Log', 'Work Orders') and not u.is_cummins else True field = IPF(text=title, default=db.domain_map_inv.get(u.domain, 'SMS'), col_db='UserGroup', table=table) add_input( field=field, items=db.domain_map.keys(), checkbox=True, cb_enabled=enabled, tooltip= 'Limit results to only those created by users in your domain.\n\ (Ensure users have been correctly initialized.)') elif 'part_name' in name: col_db = 'PartName' if 'alt' in name: col_db = 'PartNameAlt' add_input( field=IPF(text=title, col_db=col_db, like=True), checkbox=True, cb_enabled=False, tooltip='This searches both "Part Name" and "Alt Part Name".') elif name == 'part_number': add_input(field=IPF(text=title, col_db='PartNo'), checkbox=True, cb_enabled=False)