| from typing import Dict, List, Any |
|
|
| import spacy |
| from spacy.matcher import PhraseMatcher |
| from skillNer.general_params import SKILL_DB |
| from skillNer.skill_extractor_class import SkillExtractor |
|
|
| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
| from transformers import LongformerTokenizer, LongformerModel |
| import requests |
| import os |
| from dotenv import load_dotenv |
|
|
| import re |
| from datetime import datetime |
| import time |
|
|
| |
| load_dotenv('.env.local') |
|
|
| Resume_num_labels = None |
| class EndpointHandler(): |
| def __init__(self, path=""): |
| |
| |
| self.hf_token = os.getenv('HUGGINGFACE_TOKEN') |
| if not self.hf_token: |
| print("Warning: HUGGINGFACE_TOKEN environment variable not set") |
| self.Resume_label_map = { |
| "RT": 0, |
| "SST": 1, |
| "SSC": 2, |
| "AST": 3, |
| "ASC": 4, |
| "EDST": 5, |
| "EDSC": 6, |
| "SKST": 7, |
| "SKSC": 8, |
| "HST": 9, |
| "HSC": 10, |
| "CST": 11, |
| "CSC": 12, |
| "EST": 13, |
| "EJT": 14, |
| "EDT": 15, |
| "ECT": 16, |
| "EDC": 17 |
| } |
| global Resume_num_labels |
| self.Resume_num_labels = len(self.Resume_label_map) |
| Resume_num_labels = self.Resume_num_labels |
|
|
| self.Resume_labels = [ |
| {"value": "RT", "label": "Resume Title"}, |
| {"value": "SST", "label": "Summary Section Title"}, |
| {"value": "SSC", "label": "Summary Section Content"}, |
| {"value": "AST", "label": "Accomplishments Section Title"}, |
| {"value": "ASC", "label": "Accomplishments Section Content"}, |
| {"value": "EDST", "label": "Education Section Title"}, |
| {"value": "EDSC", "label": "Education Section Content"}, |
| {"value": "SKST", "label": "Skills Section Title"}, |
| {"value": "SKSC", "label": "Skills Section Content"}, |
| {"value": "HST", "label": "Highlights Section Title"}, |
| {"value": "HSC", "label": "Highlights Section Content"}, |
| {"value": "CST", "label": "Certifications Section Title"}, |
| {"value": "CSC", "label": "Certifications Section Content"}, |
| {"value": "EST", "label": "Experience Section Title"}, |
| {"value": "EJT", "label": "Experience Job Title"}, |
| {"value": "EDT", "label": "Experience Date Range Title"}, |
| {"value": "ECT", "label": "Experience Company Title"}, |
| {"value": "EDC", "label": "Experience Description Content"} |
| ] |
|
|
|
|
| self.Resume_tokenizer = LongformerTokenizer.from_pretrained("allenai/longformer-base-4096") |
| self.Resume_tokenizer.cls_token |
|
|
| |
| self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
| self.Resume_model = LongformerSentenceClassifier(num_labels=Resume_num_labels) |
| self.Resume_model.to(self.device) |
| |
| self.Resume_model.load_state_dict(torch.load(path + "/ResumeSegmentClassifier8thEpochV3.pth", map_location=self.device)) |
|
|
| |
| self.Resume_model.eval() |
| nlp = spacy.load("en_core_web_lg") |
| self.skill_extractor = SkillExtractor(nlp, SKILL_DB, PhraseMatcher) |
|
|
|
|
| def predict_resume_sections(self, model, text, tokenizer, device): |
| model.eval() |
|
|
| |
| encoding = tokenizer( |
| text, |
| return_tensors="pt", |
| truncation=True, |
| padding="max_length", |
| max_length=4096 |
| ) |
|
|
| input_ids = encoding["input_ids"].to(device) |
| attention_mask = encoding["attention_mask"].to(device) |
|
|
| |
| cls_positions = (input_ids == tokenizer.cls_token_id).nonzero(as_tuple=True)[1] |
| cls_positions = cls_positions.unsqueeze(0).to(device) |
|
|
| |
| global_attention_mask = torch.zeros_like(input_ids) |
| global_attention_mask[:, cls_positions] = 1 |
|
|
| |
| with torch.no_grad(): |
| logits = model( |
| input_ids=input_ids, |
| attention_mask=attention_mask, |
| global_attention_mask=global_attention_mask, |
| cls_positions=cls_positions |
| ) |
|
|
| logits = logits.squeeze(0) |
| probs = F.softmax(logits, dim=-1) |
| predictions = torch.argmax(probs, dim=-1) |
|
|
| return predictions.cpu().numpy() |
|
|
|
|
|
|
| def capture_sentences(self, lines): |
| combined_text = " ".join(lines) |
| sentences = re.split(r"(?<=\.)\s+|(?<=\!)\s+|(?<=\?)\s+", combined_text) |
| return [sentence.strip() for sentence in sentences if sentence.strip()] |
|
|
| def extract_resume_sections(self, text): |
| lines = text.splitlines() |
| lines = [line for line in text.splitlines() if line.strip()] |
| text = lines |
|
|
| concatenated_text = " ".join(f"{self.Resume_tokenizer.cls_token} {sentence}" for sentence in text) |
|
|
| predictions = self.predict_resume_sections(self.Resume_model, concatenated_text, self.Resume_tokenizer, self.device) |
| return predictions, text |
|
|
| def extract_resume_roles(self, text): |
| lines = text.splitlines() |
| lines = [line for line in text.splitlines() if line.strip()] |
| text = lines |
|
|
| concatenated_text = " ".join(f"{self.Resume_tokenizer.cls_token} {sentence}" for sentence in text) |
|
|
| predictions = self.predict_resume_sections(self.Resume_model, concatenated_text, self.Resume_tokenizer, self.device) |
|
|
| |
| |
| |
| |
| |
| roles = [] |
|
|
| i = -1 |
| for item in predictions[:len(predictions) - 1]: |
| |
| i+=1 |
| |
| if len(roles) == 0 and self.Resume_labels[item]['value'] == "EJT": |
| roles.append({"title": [lines[i]], "description": []}) |
| continue |
| |
| |
|
|
| |
| if self.Resume_labels[item]['value'] == "EJT": |
| |
| if len(roles[len(roles) - 1]["description"]) < 1: |
| roles[len(roles) - 1]["title"].append(lines[i]) |
| continue |
| |
| if len(roles[len(roles) - 1]["description"]) > 0: |
| roles.append({"title": [lines[i]], "description": []}) |
| continue |
|
|
| |
| if self.Resume_labels[item]['value'] == "EDC": |
| |
| if i - 1 > 0 and i + 1 < len(predictions) and self.Resume_labels[predictions[i - 1]]['value'] == "EJT" and self.Resume_labels[predictions[i + 1]]['value'] == "EJT": |
| roles[len(roles) - 1]["title"].append(lines[i]) |
| continue |
| if roles: |
| roles[-1]["description"].append(lines[i]) |
| else: |
| |
| print("Warning: Description found but no role header exists. Skipping this description.") |
|
|
| |
| if self.Resume_labels[item]['value'] != "EDC" and self.Resume_labels[item]['value'] != "EJT": |
| if i - 1 > 0 and i + 1 < len(predictions) and self.Resume_labels[predictions[i - 1]]['value'] == "EDC" and self.Resume_labels[predictions[i + 1]]['value'] == "EDC": |
| roles[-1]["description"].append(lines[i]) |
|
|
| |
| for item in roles: |
| sentences = self.capture_sentences(item['description']) |
| item['description'] = sentences |
|
|
| return roles |
| |
| def parse_date(self, date_str): |
| """Tries multiple formats to parse a date string into a datetime object. |
| |
| - Returns the current date if 'present' or 'current' is given. |
| - Tries multiple formats and prompts if ambiguous. |
| """ |
|
|
| |
| present_keywords = {"present", "current", "now", "today"} |
| if date_str.strip().lower() in present_keywords: |
| return datetime.today() |
|
|
| date_formats = [ |
| ("%b %Y", "MMM YYYY"), |
| ("%B %Y", "MMMM YYYY"), |
| ("%Y-%m-%d", "YYYY-MM-DD"), |
| ("%Y/%m/%d", "YYYY/MM/DD"), |
| ("%Y.%m.%d", "YYYY.MM.DD"), |
| ("%d-%m-%Y", "DD-MM-YYYY"), |
| ("%d/%m/%Y", "DD/MM/YYYY"), |
| ("%d.%m.%Y", "DD.MM.YYYY"), |
| ("%m-%d-%Y", "MM-DD-YYYY"), |
| ("%m/%d/%Y", "MM/DD/YYYY"), |
| ("%m.%d.%Y", "MM.DD.YYYY"), |
| ("%d %b %Y", "DD MMM YYYY"), |
| ("%d %B %Y", "DD MMMM YYYY"), |
| ("%b-%d-%Y", "MMM-DD-YYYY"), |
| ("%b/%d/%Y", "MMM/DD/YYYY"), |
| ("%B-%d-%Y", "MMMM-DD-YYYY"), |
| ("%B/%d/%Y", "MMMM/DD/YYYY"), |
| ("%d-%b-%Y", "DD-MMM-YYYY"), |
| ("%d/%b/%Y", "DD/MMM/YYYY"), |
| ("%d-%B-%Y", "DD-MMMM-YYYY"), |
| ("%d/%B/%Y", "DD/MMMM/YYYY"), |
| ("%Y", "YYYY"), |
| ("%m/%Y", "MM/YYYY"), |
| ("%m-%Y", "MM-YYYY"), |
| ("%m.%Y", "MM.YYYY"), |
| ("%Y%m%d", "YYYYMMDD"), |
| ("%d%m%Y", "DDMMYYYY"), |
| ("%m%d%Y", "MMDDYYYY"), |
| ("%Y-%b-%d", "YYYY-MMM-DD"), |
| ("%Y/%b/%d", "YYYY/MMM/DD"), |
| ("%Y-%B-%d", "YYYY-MMMM-DD"), |
| ("%Y/%B/%d", "YYYY/MMMM/DD"), |
| ("%d-%b-%y", "DD-MMM-YY"), |
| ("%d/%b/%y", "DD/MMM/YY"), |
| ("%d-%B-%y", "DD-MMMM-YY"), |
| ("%d/%B/%y", "DD/MMMM/YY"), |
| ("%d-%m-%y", "DD-MM-YY"), |
| ("%d/%m/%y", "DD/MM/YY"), |
| ("%m-%d-%y", "MM-DD-YY"), |
| ("%m/%d/%y", "MM/DD/YY"), |
| ("%A, %d %B %Y", "Day, DD MMMM YYYY"), |
| ("%a, %d %b %Y", "Day Abbr, DD MMM YYYY"), |
| ] |
|
|
| possible_dates = [] |
|
|
| for fmt, fmt_name in date_formats: |
| try: |
| parsed_date = datetime.strptime(date_str, fmt) |
| possible_dates.append((parsed_date, fmt_name)) |
| except ValueError: |
| continue |
|
|
| |
| if not possible_dates: |
| |
| return [] |
|
|
| |
| if len(possible_dates) == 1: |
| return possible_dates[0][0] |
|
|
| |
| print(f"Ambiguous date: '{date_str}' could mean:") |
| for idx, (date, fmt_name) in enumerate(possible_dates): |
| print(f"{idx + 1}. {date.strftime('%Y-%m-%d')} ({fmt_name})") |
|
|
| print("Defaulted to: ", possible_dates[0][1]) |
| return possible_dates[0][0] |
| |
| def extract_dates_from_context(self, context): |
| """Extract dates from context using the date extraction endpoint.""" |
| max_retries = 5 |
| retry_delay = 5 |
| startup_delay = 10 |
| |
| for attempt in range(max_retries): |
| try: |
| headers = { |
| "Authorization": f"Bearer {self.hf_token}" |
| } |
| response = requests.post( |
| "https://iprlg93qeghlgufi.us-east-1.aws.endpoints.huggingface.cloud", |
| json={"inputs": context}, |
| headers=headers, |
| timeout=30 |
| ) |
| |
| if response.status_code == 200: |
| return response.json() |
| elif response.status_code == 503: |
| if attempt < max_retries - 1: |
| if attempt == 0: |
| print(f"Service temporarily unavailable (503). Waiting 20 seconds... (Attempt {attempt + 1}/{max_retries})") |
| time.sleep(20) |
| else: |
| print(f"Service temporarily unavailable (503). Waiting 2 seconds... (Attempt {attempt + 1}/{max_retries})") |
| time.sleep(2) |
| continue |
| else: |
| print("Service unavailable after maximum retries") |
| return {"start_date": None, "end_date": None} |
| elif response.status_code == 404: |
| print("Endpoint not found. Please check if the endpoint URL is correct.") |
| return {"start_date": None, "end_date": None} |
| elif response.status_code == 401: |
| print("Authentication failed. Please check your Hugging Face token.") |
| return {"start_date": None, "end_date": None} |
| else: |
| print(f"Error calling date extraction endpoint: {response.status_code}") |
| print(f"Response: {response.text}") |
| return {"start_date": None, "end_date": None} |
| |
| except requests.exceptions.Timeout: |
| print(f"Request timed out. Attempt {attempt + 1}/{max_retries}") |
| if attempt < max_retries - 1: |
| time.sleep(retry_delay) |
| continue |
| return {"start_date": None, "end_date": None} |
| except Exception as e: |
| print(f"Exception while calling date extraction endpoint: {str(e)}") |
| if attempt < max_retries - 1: |
| time.sleep(retry_delay) |
| continue |
| return {"start_date": None, "end_date": None} |
| |
| return {"start_date": None, "end_date": None} |
| |
| def extract_length(self, start_date, end_date): |
| """ |
| Args: |
| start_date (datetime): The earlier date. |
| end_date (datetime): The later date. |
| |
| Returns: |
| int: Number of full months between the dates. |
| """ |
| try: |
| if start_date > end_date: |
| |
| return 0 |
| except: |
| return 0 |
|
|
| |
| year_diff = end_date.year - start_date.year |
| month_diff = end_date.month - start_date.month |
|
|
| |
| total_months = year_diff * 12 + month_diff |
| return total_months |
|
|
| def label_resume(self, text): |
| results = self.extract_resume_roles(text) |
| for item in results: |
| |
| context = (" ".join(item["title"])) |
| dates = self.extract_dates_from_context(context) |
| date_started = dates.get("start_date") |
| date_ended = dates.get("end_date") |
|
|
| |
| try: |
| date_started_formatted = self.parse_date(date_started) if date_started else None |
| except ValueError: |
| date_started_formatted = None |
|
|
| try: |
| date_ended_formatted = self.parse_date(date_ended) if date_ended else None |
| except ValueError: |
| date_ended_formatted = None |
|
|
| try: |
| role_length = self.extract_length(date_started_formatted, date_ended_formatted) |
| except: |
| role_length = 0 |
| item["dates"] = {"date_started": date_started, "date_ended": date_ended} |
| item["role_length"] = role_length |
|
|
| |
| item["skills"] = [] |
| seen = set() |
| annotations = self.skill_extractor.annotate(" ".join(item["description"])) |
| if 'results' in annotations and 'full_matches' in annotations['results']: |
| for result in annotations['results']['full_matches']: |
| |
| skill_info = SKILL_DB.get(result["skill_id"], {}) |
| skill_name = skill_info.get('skill_name', 'Unknown Skill') |
| if skill_name not in seen: |
| seen.add(skill_name) |
| item["skills"].append({'name': skill_name, 'skill_id': result["skill_id"]}) |
| if 'results' in annotations and 'ngram_scored' in annotations['results']: |
| for result in annotations['results']['ngram_scored']: |
| if result['score'] >= 1: |
| |
| skill_info = SKILL_DB.get(result["skill_id"], {}) |
| skill_name = skill_info.get('skill_name', 'Unknown Skill') |
| if skill_name not in seen: |
| seen.add(skill_name) |
| item["skills"].append({'name': skill_name, 'skill_id': result["skill_id"]}) |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| return results |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| def __call__(self, data: Dict[str, Any]) -> List[Dict[str, Any]]: |
| """ |
| data args: |
| inputs (:obj: `str` | `PIL.Image` | `np.array`) |
| kwargs |
| Return: |
| A :obj:`list` | `dict`: will be serialized and returned |
| """ |
| text = data['inputs'] |
| |
| |
| label_resume = self.label_resume(text) |
| return label_resume |
| |
|
|
| class LongformerSentenceClassifier(nn.Module): |
| def __init__(self, model_name="allenai/longformer-base-4096", num_labels=Resume_num_labels): |
| """ |
| Custom Longformer model for sentence classification. |
| |
| Args: |
| model_name (str): Hugging Face Longformer model. |
| num_labels (int): Number of possible sentence labels. |
| """ |
| super(LongformerSentenceClassifier, self).__init__() |
| self.longformer = LongformerModel.from_pretrained(model_name) |
| self.classifier = nn.Linear(self.longformer.config.hidden_size, num_labels) |
|
|
| def forward(self, input_ids, attention_mask, global_attention_mask, cls_positions): |
| """ |
| Forward pass for sentence classification. |
| |
| Args: |
| input_ids (Tensor): Tokenized input IDs, shape (batch_size, max_length) |
| attention_mask (Tensor): Attention mask, shape (batch_size, max_length) |
| global_attention_mask (Tensor): Global attention mask, shape (batch_size, max_length) |
| cls_positions (List[Tensor]): Indices of `[CLS]` tokens for each batch element. |
| """ |
| outputs = self.longformer( |
| input_ids=input_ids, |
| attention_mask=attention_mask, |
| global_attention_mask=global_attention_mask |
| ) |
|
|
| last_hidden_state = outputs.last_hidden_state |
| cls_positions = cls_positions.view(input_ids.shape[0], -1) |
| cls_embeddings = last_hidden_state.gather(1, cls_positions.unsqueeze(-1).expand(-1, -1, last_hidden_state.size(-1))) |
| logits = self.classifier(cls_embeddings) |
|
|
| return logits |
|
|
|
|
|
|
| if __name__ == "__main__": |
| |
| my_handler = EndpointHandler(path=".") |
|
|
| |
| payload = {"inputs": """ |
| CASHIER |
| |
| |
| Professional Summary |
| Results-oriented, strategic sales professional with two years in the Retail industry. Cashier who is highly energetic, outgoing and detail-oriented. Handles multiple responsibilities simultaneously while providing exceptional customer service. Reliable and friendly team member who quickly learns and masters new concepts and skills. Passionate about helping customers and creating a satisfying shopping experience. |
| |
| |
| Core Qualifications |
| • Excellent multi-tasker |
| • Strong communication skills |
| • Flexible schedule |
| • Proficient in MS Office |
| Cash handling accuracy |
| Mathematical aptitude |
| Organized |
| Time management |
| Detail-oriented |
| |
| |
| Experience |
| Cashier |
| October 2014 to Current |
| Company Name - City , State |
| • Receive payment by cash, check, credit cards, vouchers, or automatic debits. |
| • Issue receipts, refunds, credits, or change due to customers. |
| • Assist customers by providing information and resolving their complaints. |
| • Establish or identify prices of goods, services or admission, and tabulate bills using calculators, cash registers, or optical price scanners. |
| • Greet customers entering establishments. |
| • Answer customers' questions, and provide information on procedures or policies. |
| • Process merchandise returns and exchanges. |
| • Maintain clean and orderly checkout areas and complete other general cleaning duties, such as mopping floors and emptying trash cans. |
| • Stock shelves, and mark prices on shelves and items. |
| • Count money in cash drawers at the beginning of shifts to ensure that amounts are correct and that there is adequate change. |
| • Calculate total payments received during a time period, and reconcile this with total sales. |
| • Monitor checkout stations to ensure that they have adequate cash available and that they are staffed appropriately. |
| • Assist with duties in other areas of the store, such as monitoring fitting rooms or bagging and carrying out customers' items. |
| • Sort, count, and wrap currency and coins. |
| • Compute and record totals of transactions. |
| • Compile and maintain non-monetary reports and records. |
| • Weigh items sold by weight to determine prices. |
| • Cash checks for customers. |
| |
| Inbound/Return |
| June 2014 to September 2014 |
| Company Name - City , State |
| Changed equipment over to new product.Maintained proper stock levels on a line.Helped achieve company goals by supporting production workers. |
| |
| Cashier |
| February 2014 to June 2014 |
| Company Name - City , State |
| • Receive payment by cash, check, credit cards, vouchers, or automatic debits. |
| • Issue receipts, refunds, credits, or change due to customers. |
| • Assist customers by providing information and resolving their complaints. |
| • Establish or identify prices of goods, services or admission, and tabulate bills using calculators, cash registers, or optical price scanners. |
| • Greet customers entering establishments. |
| • Answer customers' questions, and provide information on procedures or policies. |
| • Process merchandise returns and exchanges. |
| • Maintain clean and orderly checkout areas and complete other general cleaning duties, such as mopping floors and emptying trash cans. |
| • Stock shelves, and mark prices on shelves and items. |
| • Count money in cash drawers at the beginning of shifts to ensure that amounts are correct and that there is adequate change. |
| • Calculate total payments received during a time period, and reconcile this with total sales. |
| • Monitor checkout stations to ensure that they have adequate cash available and that they are staffed appropriately. |
| • Assist with duties in other areas of the store, such as monitoring fitting rooms or bagging and carrying out customers' items. |
| • Sort, count, and wrap currency and coins. |
| • Compute and record totals of transactions. |
| • Compile and maintain non-monetary reports and records. |
| • Weigh items sold by weight to determine prices. |
| • Cash checks for customers. |
| |
| Apparel Associate |
| January 2014 to February 2014 |
| Company Name - City , State |
| • Greet customers and ascertain what each customer wants or needs. |
| • Describe merchandise and explain use, operation, and care of merchandise to customers. |
| • Recommend, select, and help locate or obtain merchandise based on customer needs and desires. |
| • Compute sales prices, total purchases and receive and process cash or credit payment. |
| • Answer questions regarding the store and its merchandise. |
| • Maintain knowledge of current sales and promotions, policies regarding payment and exchanges, and security practices. |
| • Maintain records related to sales. |
| • Watch for and recognize security risks and thefts, and know how to prevent or handle these situations. |
| • Inventory stock and requisition new stock. |
| • Help customers try on or fit merchandise. |
| • Clean shelves, counters, and tables. |
| • Exchange merchandise for customers and accept returns. |
| • Open and close cash registers, performing tasks such as counting money, separating charge slips, coupons, and vouchers, balancing cash drawers, and making deposits. |
| |
| Apparel Associate |
| October 2013 to December 2013 |
| Company Name - City , State |
| • Greet customers and ascertain what each customer wants or needs. |
| • Describe merchandise and explain use, operation, and care of merchandise to customers. |
| • Recommend, select, and help locate or obtain merchandise based on customer needs and desires. |
| • Compute sales prices, total purchases and receive and process cash or credit payment. |
| • Answer questions regarding the store and its merchandise. |
| • Maintain knowledge of current sales and promotions, policies regarding payment and exchanges, and security practices. |
| • Maintain records related to sales. |
| • Watch for and recognize security risks and thefts, and know how to prevent or handle these situations. |
| • Inventory stock and requisition new stock. |
| • Help customers try on or fit merchandise. |
| • Clean shelves, counters, and tables. |
| • Exchange merchandise for customers and accept returns. |
| • Open and close cash registers, performing tasks such as counting money, separating charge slips, coupons, and vouchers, balancing cash drawers, and making deposits. |
| |
| Cashier |
| August 2012 to August 2013 |
| Company Name - City , State |
| • Receive payment by cash, check, credit cards, vouchers, or automatic debits. |
| • Issue receipts, refunds, credits, or change due to customers. |
| • Assist customers by providing information and resolving their complaints. |
| • Establish or identify prices of goods, services or admission, and tabulate bills using calculators, cash registers, or optical price scanners. |
| • Greet customers entering establishments. |
| • Answer customers' questions, and provide information on procedures or policies. |
| • Process merchandise returns and exchanges. |
| • Maintain clean and orderly checkout areas and complete other general cleaning duties, such as mopping floors and emptying trash cans. |
| • Stock shelves, and mark prices on shelves and items. |
| • Count money in cash drawers at the beginning of shifts to ensure that amounts are correct and that there is adequate change. |
| • Calculate total payments received during a time period, and reconcile this with total sales. |
| • Monitor checkout stations to ensure that they have adequate cash available and that they are staffed appropriately. |
| • Assist with duties in other areas of the store, such as monitoring fitting rooms or bagging and carrying out customers' items. |
| • Sort, count, and wrap currency and coins. |
| • Compute and record totals of transactions. |
| • Compile and maintain non-monetary reports and records. |
| • Weigh items sold by weight to determine prices. |
| • Cash checks for customers. |
| |
| |
| Education |
| 5 2013 |
| Member of FFA, FCA, Pep Club, and mentoring children from one of the public elementary schools |
| |
| |
| Skills |
| • Calculators |
| • Cash registers |
| • Credit, debit, checks and money |
| • Inventory |
| • Sales, scanners, tables |
| """} |
| |
|
|
| |
| non_holiday_pred=my_handler(payload) |
| |
|
|
| |
| print(non_holiday_pred) |
| |