JAVA OOP concept issue – Code Review Stack Exchange

Hello guys could you help me please how to render this code as OOP?

I want to improve this code and follow the OOP anyone could help me, please

thank you in advance

this is class Expression

class Expression {
        public int type;
        public int value;
        public Expression leftOp;
        public Expression rightOp;
    
        public Expression(int type, int value, Expression leftOp, Expression rightOp) {
            this.type = type;
            this.value = value;
            this.leftOp = leftOp;
            this.rightOp = rightOp;
        }
    }


    class Arith {
    

/** Constantes pour representer les types */
        public static final int TYPE_NUMBER = 1;
        public static final int TYPE_SUM = 2;
        public static final int TYPE_PROD = 3;

    

    public static void main(String() args) {
     
            Expression term = new Expression(TYPE_SUM, 0, new Expression(TYPE_NUMBER, 3, null, null), new Expression(
                    TYPE_PROD, 0, new Expression(TYPE_NUMBER, 2, null, null), new Expression(TYPE_NUMBER, 5, null, null)));
            System.out.println(evaluate(term));
        }
    
        /** Evalue recursivement the expression */
        public static int evaluate(Expression term) {
            switch (term.type) {
            case TYPE_NUMBER:
                return term.value;
            case TYPE_SUM:
                return evaluate(term.leftOp) + evaluate(term.rightOp);
            case TYPE_PROD:
                return evaluate(term.leftOp) * evaluate(term.rightOp);
            default:
                return 0; 
            }
        }
    }

python – How to organize and formating my code while maintaining all OOP principles

I want to create a snake game with pygame in python That can fit for multiple players. at the start, i created classes for snake, button and snake-game… Now i want to create an graficall GUI with pygame that help the user to define the game properties, like a settings screen for define things like the snakes speed, and if the snakes will be disqualified when they coming out of the screen, or that they will coming out of the other side of him. Home screen containing a hello message and buttons that will determine how many players will there are in the game. Screen that define the snakes properties like color and keys. And of course buttons on each screen that let the user switch between the screens. But i am not sure where to execute all these screens, In what class? Where should it be? Maybe in a new class? Maybe in single functions? Where is the best place to implement it while maintaining OOP principles like Single responsibility, interface separation, maintaining high cohesion and low coupling…? And how can I always know where this place is? How can I design my code well? Any answer you have will greatly help me!

This is my code:

class Snake:
    drafting_score_mess = "{name} score: {score}"

    def __init__(self, name, color: Tuple(int, int, int), speed: float, start_point: Tuple(int, int),
                 block, keys, disqualification_violation_boundaries=True):
        self.name = name
        self._color = color

        self._speed = 1 / speed
        self._last_measure = time.time()
        self.disqualification_violation_boundaries = disqualification_violation_boundaries
        self.score = 0
        self.disqualified = False
        self._length = ()
        self._block = block

        self.start_point = start_point
        self._x = start_point(0)
        self._y = start_point(1)
        self._x_cha = 0
        self._y_cha = 0
        self._d = -1

        keys = {k.lower(): v for k, v in keys.items()}
        self._l_key = keys("left")
        self._r_key = keys("right")
        self._u_key = keys("up")
        self._d_key = keys("down")

    def update_pos(self, screen: pygame.Surface) -> None:
        if not self.disqualified:
            pressed_keys = pygame.key.get_pressed()
            if pressed_keys(self._l_key) and self._d != 0:
                self._x_cha = -self._block
                self._y_cha = 0
                self._d = 0
            elif pressed_keys(self._r_key) and self._d != 0:
                self._x_cha = self._block
                self._y_cha = 0
                self._d = 0
            elif pressed_keys(self._u_key) and self._d != 1:
                self._y_cha = -self._block
                self._x_cha = 0
                self._d = 1
            elif pressed_keys(self._d_key) and self._d != 1:
                self._y_cha = self._block
                self._x_cha = 0
                self._d = 1

        self._x = self._x // 10 * 10
        self._y = self._y // 10 * 10
        current_measure = time.time()
        if current_measure - self._last_measure >= self._speed:
            self._x += self._x_cha
            self._y += self._y_cha

            self._length.append((self._x, self._y))
            self._length = self._length(len(self._length) - (self.score + 1):)
            self._last_measure = current_measure

        for snake_block_pos in self._length:
            pygame.draw.rect(screen, self._color,
                             (snake_block_pos(0), snake_block_pos(1), self._block, self._block))

    def dis_score(self, font: pygame.font.Font, pos: Tuple(int, int), screen: pygame.Surface) -> None:
        score_mass = font.render(self.drafting_score_mess.format(name=self.name, score=self.score),
                                 True, self._color)
        screen.blit(score_mass, pos)

    def update_status_if_encountered_himself(self):
        if (self._x, self._y) in self._length(:-1):
            self.disqualified = True

    def update_status_if_encountered_margins(self, scr_size: Tuple(int, int)):
        x, y = scr_size
        if self.disqualification_violation_boundaries:
            if self._x >= x or self._x < 0 or self._y >= y or self._y < 0:
                self.disqualified = True
        else:
            if self._x > x:
                self._x = 0
            elif self._x < 0:
                self._x = x
            elif self._y > y:
                self._y = 0
            elif self._y < 0:
                self._y = y

    def get_head_pos(self):
        return tuple((self._x, self._y))

    def reset(self, start_point: Tuple(int, int) = None):
        if start_point is None:
            start_point = self.start_point
        keys = {"up": self._u_key, "down": self._d_key, "right": self._r_key, "left": self._l_key}
        Snake.__init__(self, self.name, self._color, 1 / self._speed, start_point, self._block, keys,
                       self.disqualification_violation_boundaries)

    def get_all_positions(self):
        return self._length

    @property
    def speed(self):
        return int(1 / self._speed)

    @speed.setter
    def speed(self, new_speed):
        if not (10 < new_speed < 150):
            raise ValueError(f"speed need to be between 10-100 got {new_speed}")
        self._speed = 1 / new_speed

    def __gt__(self, other) -> bool:
        return self.score > other

    def __ge__(self, other) -> bool:
        return self.score >= other

    def __lt__(self, other) -> bool:
        return self.score < other

    def __le__(self, other) -> bool:
        return self.score <= other

    def __ne__(self, other) -> bool:
        return self.score != other

    def __eq__(self, other) -> bool:
        return self.score == other


class Button:
    def __init__(self, screen, pos_and_size: Tuple(int, int, int, int) or List(int, int, int, int), func_in_pressed,
                 background: Union(str, bytes, PathLike) or Tuple(int, int, int),
                 text: AnyStr = None, text_font: pygame.font.Font = None, text_color: Tuple(int, int, int) = None,
                 text_align_center=False, brightness_if_mouse_on=50, *func_parameters):
        """
        :param text: The text on the button
        :param pos_and_size: The position and the size of the
        button in format - (x, y, width, height). can be tuple or list.
        :param func_in_pressed: func that calling when the the button pressed
        :param background: Color or image path - tuple or list of three integers or string with path
        """
        self.x = pos_and_size(0)
        self.y = pos_and_size(1)
        self.width = pos_and_size(2)
        self.height = pos_and_size(3)

        self._screen = screen
        self.text = text
        self._font = text_font
        self.text_color = text_color
        self.text_align_center = text_align_center
        self.background = background

        self._brightness = brightness_if_mouse_on
        self._func = func_in_pressed
        self._func_parameters = func_parameters

    def check_button(self):
        if_mouse_click = pygame.mouse.get_pressed(3)(0)
        click_pos = pygame.mouse.get_pos()
        if if_mouse_click and self.x <= click_pos(0) <= self.x + self.width and self.y <= click_pos(1) <= self.y + 
                self.height:
            return self._func(*self._func_parameters)

    def display(self):
        mouse = pygame.mouse.get_pos()
        if isinstance(self.background, list) or isinstance(self.background, tuple):
            if self.x <= mouse(0) <= self.x + self.width and self.y <= mouse(1) <= self.y + self.height:
                color_light = (255 if i + self._brightness >= 255 else i + self._brightness for i in self.background)
                pygame.draw.rect(self._screen, color_light, (self.x, self.y, self.width, self.height))
            else:
                pygame.draw.rect(self._screen, self.background, (self.x, self.y, self.width, self.height))

        elif isinstance(self.background, str):
            image = pygame.image.load(self.background)
            image = pygame.transform.scale(image, (self.width, self.height))

            bright_image = image.copy()
            bright_image.fill((self._brightness, self._brightness, self._brightness),
                              special_flags=pygame.BLEND_RGB_ADD)

            #
            if self.x <= mouse(0) <= self.x + self.width and self.y <= mouse(1) <= self.y + self.height:
                self._screen.blit(bright_image, (self.x, self.y))
            else:
                self._screen.blit(image, (self.x, self.y))

        else:
            raise ValueError("background have to be image path or RBG color.")

        if self.text is not None and self._font is not None and self.text_color is not None:
            text = self._font.render(self.text, True, self.text_color)
            if self.text_align_center:
                text_x = self.x + self.width // 2 - self._font.size(self.text)(0) // 2
            else:
                text_x = self.x
            self._screen.blit(text, (text_x, self.y))


class Display:
    @staticmethod
    def update_display():
        pygame.event.pump()
        pygame.display.update()
        time.sleep(0.009)


class SnakesArrayManager:
    def __init__(self, snakes_lst):
        self._snakes = snakes_lst

    def update_snakes_pos(self, screen):
        for snake in self._snakes:
            snake.update_pos(screen)

    def update_snakes_status_if_encountered_margins(self, screen: pygame.Surface):
        width, height = screen.get_width(), screen.get_height()
        for snake in self._snakes:
            snake.update_status_if_encountered_margins((width, height))

    def update_snakes_status_if_encountered_by_themselves(self):
        for snake in self._snakes:
            snake.update_status_if_encountered_himself()

    def update_snakes_status_if_encountered_others(self):
        for ind, snake in enumerate(self._snakes):
            for other in self._snakes(:ind) + self._snakes(ind + 1:):
                if snake.get_head_pos() in other.get_all_positions():
                    snake.disqualified = True

    def display_snakes_score_in_rows(self, screen, font: pygame.font.Font, first_line_pos_start: Tuple(int, int),
                                     line_height: int):
        x_row = first_line_pos_start(0)
        y_row = first_line_pos_start(1)
        for snake in self._snakes:
            snake.dis_score(font, (x_row, y_row), screen)
            y_row += line_height + 5

    def __str__(self):
        return f"SnakesArrayManager({self._snakes})"

    def __getitem__(self, item):
        return self._snakes(item)


class SnakeGame(Display, SnakesArrayManager):
    def __init__(self, screen: pygame.Surface, snakes: List(Snake)):
        super().__init__(snakes)
        self._screen = screen
        self._foods_lst = ()

    def add_food(self, block: int):
        x = random.randrange(0, self._screen.get_width(), block)
        y = random.randrange(0, self._screen.get_height(), block)
        self._foods_lst.append((x, y))

    def display_foods(self, color: Tuple(int, int, int), block):
        for x_food, y_food in self._foods_lst:
            if x_food >= self._screen.get_width() or y_food >= self._screen.get_height():
                self._foods_lst.remove((x_food, y_food, block))

            pygame.draw.rect(self._screen, color, (x_food, y_food, block, block))

    def update_snakes_score_by_food(self):
        for snake in self._snakes:
            snake_pos = snake.get_head_pos()
            if snake_pos in self._foods_lst:
                self._foods_lst.remove(snake_pos)
                snake.score += 1

    def game_over_display(self, game_over_mass_text: str, game_over_font: pygame.font.Font,
                          game_over_mass_color: Tuple(int, int, int),
                          score_table_font: pygame.font.Font, mass_align_center: bool = True):
        width, height = self._screen.get_width(), self._screen.get_height()

        game_over_mass_size = game_over_font.size(game_over_mass_text)
        if mass_align_center:
            game_over_mass_x = width // 2 - game_over_mass_size(0) // 2
        else:
            game_over_mass_x = 0
        game_over_mass = game_over_font.render(game_over_mass_text, True, game_over_mass_color)
        self._screen.blit(game_over_mass, (game_over_mass_x, height // 4))

        score_row_size = score_table_font.size(Snake.drafting_score_mess.format(name="      ", score="    "))
        if mass_align_center:
            score_table_x = width // 2 - score_row_size(0) // 2
        else:
            score_table_x = 0

        self.display_snakes_score_in_rows(self._screen, score_table_font,
                                          (score_table_x, height // 4 + game_over_mass_size(1) + 10), score_row_size(1))

    def restart(self):
        for snake in self._snakes:
            snake.reset()
        self.__init__(self._screen, *self._snakes)

    @property
    def game_over(self):
        snakes_lost = (snake.disqualified for snake in self._snakes)
        if False not in snakes_lost:
            return True
        else:
            return False

If you have any other comments on the code (I’m sure you have) please write as well. Really thanks for the help.

object oriented – Python code for simple Vending Machine simulation implementing OOP principles

I am recently learning OOP concepts and thought that implementing a simple workflow simulating a vending machine would help me put what I’ve learnt into practice. I am open to criticisms and feedback, and am mainly trying to get some comments regarding:

  • Best practices for OOP
  • File structure
  • Data encapsulation
  • Unit testing practices
  • General architecture advice for OOP

The project simulates a vending machine dispensing drinks for customers for X hours (user input), given that arrival times follow a Poisson(K) distribution and an initial stock list (user input). The project also produces a sales report for the given simulation.

The link to my the entire github repository is here: https://github.com/jtsw1990/oop_vending_machine

Below is a snippet of vending_machine.py, which is the class to simulate the behavior of a typical vending machine. Any help is appreciated not just for this snippet, but the other classes in the repo as well like the customer class.

import csv
import numpy as np


class DataReader:
    '''
    Object to read in initial drinks data set
    to be used as input in VendingMachine class
    '''

    def __init__(self, filepath):
        self.df = ()
        with open(filepath, "r") as file:
            my_reader = csv.reader(file, delimiter=",")
            next(my_reader)
            for row in my_reader:
                self.df.append(row)


class VendingMachine:
    '''
    Insert doc string here
    '''

    def __init__(self, max_capacity):
        print("__init__ is being called here")
        self.max_capacity = max_capacity
        self.current_earnings = 0
        self.current_stock = 0
        self.stock_list = None
        self.drinks_displayed = None

    def __repr__(self):
        print("__repr__ was being called here")
        return "VendingMachine({!r})".format(self.max_capacity)

    @property
    def max_capacity(self):
        print("max_cap property being called here")
        return self.__max_capacity

    @max_capacity.setter
    def max_capacity(self, max_capacity):
        print("max_cap setter called here")
        if not isinstance(max_capacity, (int, float)):
            raise TypeError("Please enter an integer value")
        elif max_capacity < 0:
            raise ValueError("Capacity cannot be negative")
        elif max_capacity % 1 != 0:
            raise TypeError("Please enter an integer value")
        else:
            self.__max_capacity = max_capacity

    def load_drinks(self, filepath):
        self.drink_list = DataReader(filepath).df
        if self.stock_list is None:
            self.stock_list = {
                row(0): (float(row(1)), int(row(2)))
                for row in self.drink_list
            }
        current_stock = sum((value(-1) for key, value in self.stock_list.items()))
        if current_stock > self.max_capacity:
            raise ValueError("Loaded drinks past capacity")
        else:
            self.current_stock = current_stock

    def display_stock(self):
        self.drinks_displayed = (
            x(0) for x in list(self.stock_list.items()) if x(-1)(-1) > 0
        )
        return self.drinks_displayed

    def dispense_drink(self, drink_name):
        '''
        Method to simulate a vending machine object
        dispensing a drink. Returns drink_name as a string if available.
        Returns None if out of stock.
        '''
        try:
            if self.stock_list(drink_name)(-1) > 0:
                self.stock_list(drink_name)(-1) -= 1
                self.current_earnings = np.round(
                    self.current_earnings + self.stock_list(drink_name)(0), 2)
                self.current_stock = sum(
                    (value(-1) for key, value in self.stock_list.items()))
                return drink_name
            else:
                return None
        except KeyError:
            print("Machine out of stock")
            return None


if __name__ == "__main__":
    test = VendingMachine(200)
    test.load_drinks("drinks_list.csv")
    test.display_stock()
    for i in range(150):
        test.dispense_drink("coke")
        test.dispense_drink("ice_lemon_tea")
        test.dispense_drink("fanta_orange")
        test.dispense_drink("fanta_grape")
        test.dispense_drink("sprite")
        test.dispense_drink("pepsi")
        test.dispense_drink("mountain_dew")
    print(test.stock_list)
    print(test.display_stock())
```

object oriented – Need a small push converting fully functioning Python REST client script to OOP

I have a fully functioning ETL project I’ve deployed for a client. Works great, pulls data from an API, parses JSON, normalizes it, and updates a database for some dashboards.

I would like to make it class-based. I just can’t find a place to start. If you could get me going, I can run with it. I’m familiar with OOP in Python, but have yet to implement the principles in a project.

I feel I’ve commented the code very well, but it is a bit long (can benefit a lot from OOP to reuse code).

Here is the API it hits: Link

Here is my repo: Link

# -*- coding: utf-8 -*-
"""
Created on Wed Oct  2 21:39:59 2019

@author: Brian-Admin
"""

from pandas.io.json import json_normalize
from requests.auth import HTTPBasicAuth
import datetime
import requests
import sqlite3
import logging
import pandas
import numpy
import json
import sys


def getBearerAndScope(store):
    BASE_URL = 'https://cba.tekmetric.com/api/v1/'
    r = requests.post(url=BASE_URL + 'oauth/token',
                      headers={'Content-Type':'application/x-www-form-urlencoded;charset=UTF-8'},
                      auth=HTTPBasicAuth(creds(store)('client_id'),
                                         creds(store)('client_secret')),
                      data='grant_type=client_credentials'
                      )
    if r.status_code != 200:
        return 'Something went wrong, error: ' + str(r.status_code)
    else:
        return (r.json()('access_token'), r.json()('scope'))

def str2time(timeStr):
    t = datetime.datetime.strptime(timeStr.replace('T', ' ')(:-1),'%Y-%m-%d %H:%M:%S')
    return t

def time2str(timeObj):
    s = str(timeObj).replace(' ','T') + 'Z'
    return s


def getPrevCallTime(f):
    with open(f,'r') as x:
        d = json.load(x)
    prevCallTime = str2time(d('callTime'))
    prevCallTime = prevCallTime + datetime.timedelta(seconds=1)
    prevCallTime = time2str(prevCallTime)
    return prevCallTime

def writeThisCallTime(f,time):
    d = {'callTime':time}
    with open(f,'w') as x:
        json.dump(d,x)    
    



  
def singleCall(paramsDict, tknBearer, endpoint):
    BASE_URL = 'https://cba.tekmetric.com/api/v1/'
    headersDict = {'Authorization': 'Bearer ' + tknBearer}
    r = requests.get(url=BASE_URL + endpoint, headers=headersDict, params=paramsDict)
    return r

# return a list of series to be used for Null checks on Sublets, Items, Discounts,
# Fees, and CustomerConcerns (first element in the returned tuple) and also
# return a dataframe that is cleaned and prepped to be inserted into sql db as
# second element in the returned tuple
def parseJSON_RO(data):
    
    try:
    
        # dont attempt to process if JSON content is empty, return tuple with 2 Nones
        if len(data('content')) != 0:
            
            repairOrders_df = json_normalize(data=data('content'))
                  
            repairOrders_df.rename(
                    columns={'id':'repairOrderId',
                            'repairOrderLabel.code':'repairOrderLabelCode',
                            'repairOrderLabel.id':'repairOrderLabelId',
                            'repairOrderLabel.name':'repairOrderLabelName',
                            'repairOrderLabel.status.code':'repairOrderLabelStatusCode',
                            'repairOrderLabel.status.id':'repairOrderLabelStatusId',
                            'repairOrderLabel.status.name':'repairOrderLabelStatusName',
                            'repairOrderStatus.code':'repairOrderStatusCode',
                            'repairOrderStatus.id':'repairOrderStatusId',
                            'repairOrderStatus.name':'repairOrderStatusName'
                            },
                     inplace=True)
            
            seriesList = (repairOrders_df('sublets'),
                          repairOrders_df('fees'),
                          repairOrders_df('discounts'),
                          repairOrders_df('customerConcerns'))
            
            repairOrders_df = repairOrders_df.drop(axis=1,
                                                    columns=('customerConcerns',
                                                             'estimateUrl',
                                                             'fees',
                                                             'discounts',
                                                             'inspectionUrl',
                                                             'invoiceUrl',
                                                             'jobs',
                                                             'sublets',
                                                             'repairOrderLabel.status'))
        
            return (seriesList,repairOrders_df)
        else:
            logging.info('Empty dataset detected for ROs')
            return (None, None)
        
    except:
        logging.warning('Script failed at parseJSON_RO() function')

# data will always be JSON, ser is a series from root dataframe, 
# repairOrders_df in this case
def parseJSON_SubletsItems(ser,data):        
    

        try:
            # at least one record in the result set must have sublet information,
            # or json_normalize will not produce a dataframe. Attempting to drop
            # a column from a dataframe that does not have said column will result
            # in error, so before entering this area, we check to see if there are
            # any sublets. This pattern is repeated for all derived dataframes
            
            subletBool = sum((len(x) for x in ser)) != 0
            
            # return a tuple of two NoneTypes if subletBool is False
            if subletBool:
                repairOrders_sublets_df = json_normalize(record_path=('sublets'), 
                                                             data=data('content'), 
                                                             meta='id', 
                                                             meta_prefix='repairOrder.'
                                                             )
                
                
                
                repairOrders_sublets_df.rename(columns={'id':'subletId',
                                                        'repairOrder.id':'repairOrderId'
                                                        },inplace=True)
                
                
                
                if 'vendor.id' in repairOrders_sublets_df:
                    repairOrders_sublets_df.rename(columns={'vendor.id':'vendorId','vendor.name':'vendorName'}, inplace=True)
                    
                    # keepCols_sublets added 12.2.20 due to the proven unpredictability of TekMetrics wild itches
                    # to add json attributes without letting anyone on earth know they're doing so, please and ty
                    keepCols_sublets = ('subletId',
                                        'name',
                                        'vendorId',
                                        'authorized',
                                        'authorizedDate',
                                        'selected',
                                        'note',
                                        'price',
                                        'cost',
                                        'repairOrderId',
                                        'vendorName')
                    repairOrders_sublets_df = repairOrders_sublets_df(keepCols_sublets)
                    
                    # oh but wait, the update included a second repairOrderId column, so now there's two of them
                    # keepCols_sublets doesnt catch this, so I'm removing the second one manually, please and ty
                    # btw, tildes in pandas inverts booleans
                    repairOrders_sublets_df = repairOrders_sublets_df.loc(:,~repairOrders_sublets_df.columns.duplicated())
                    
                    
                    
                else:
                    
                    keepCols_sublets = ('subletId',
                                        'name',
                                        'authorized',
                                        'authorizedDate',
                                        'selected',
                                        'note',
                                        'price',
                                        'cost',
                                        'repairOrderId')
                    repairOrders_sublets_df = repairOrders_sublets_df(keepCols_sublets)
                    repairOrders_sublets_df = repairOrders_sublets_df.loc(:,~repairOrders_sublets_df.columns.duplicated())
                    
                    # still need these two columns to match sqlite table structure
                    repairOrders_sublets_df('vendorName') = numpy.nan
                    repairOrders_sublets_df('vendorId') = numpy.nan
                    
                
                
                
                 ### wait for response on 'can items exist without sublets/vendors'? ###
                 
                repairOrders_items_df = json_normalize(
                    record_path=('sublets','items'), 
                    data=data('content'), 
                    meta=('id',('sublets','id')),
                    meta_prefix='ro.sublet.',
                    errors='ignore')
                
                if repairOrders_items_df.shape(0) == 0:
                    repairOrders_items_df = None
                else:
                    repairOrders_items_df = repairOrders_items_df.drop(axis=1,columns=('ro.sublet.id'))
                    repairOrders_items_df.rename(columns={'id':'itemId','ro.sublet.sublets.id':'subletId'},inplace=True)
    
                return (repairOrders_sublets_df, repairOrders_items_df)
            
            else:
                return (None, None)
        except:
            logging.warning('Script failed at parseJSON_SubletsItems() function')
        
def parseJSON_ROFees(ser,data):
    

    try:
        feesBool_ros = sum((len(x) for x in ser)) != 0
        
        if feesBool_ros:
        
            repairOrders_fees_df = json_normalize(record_path=('fees'), 
                                                      data=data('content'), 
                                                      meta='id', 
                                                      meta_prefix='ro.'
                                                      )
            
            repairOrders_fees_df.rename(columns={'id':'feeId',
                                                      'ro.id':'parentId'},
                                            inplace=True)
            
            repairOrders_fees_df('parentType') = 'RO'
            
            return repairOrders_fees_df
        else:
            return None
    except:
        logging.warning('Script failed at parseJSON_ROFees() function')

def parseJSON_RODisc(ser,data):   
    
    try:
     
        discBool_ros = sum((len(x) for x in ser)) != 0
        
        if discBool_ros:
        
            repairOrders_discounts_df = json_normalize(record_path=('discounts'), 
                                                           data=data('content'), 
                                                           meta='id', 
                                                           meta_prefix='ro.'
                                                           )
            
            repairOrders_discounts_df.rename(columns={'id':'discountId',
                                                      'ro.id':'parentId'},
                                            inplace=True)
            
            repairOrders_discounts_df('parentType') = 'RO'
            
            return repairOrders_discounts_df
        else:
            return None
    except:
        logging.warning('Script failed at parseJSON_RODisc() function')    
        
def parseJSON_CustConc(ser,data):
    
    try:      
        custConcBool = sum((len(x) for x in ser)) != 0  
        
        if custConcBool:
            repairOrders_customerConcerns_df = json_normalize(
                    record_path=('customerConcerns'), 
                    data=data('content'), 
                    meta='id', 
                    meta_prefix='ro.')
            
            repairOrders_customerConcerns_df.rename(
                    columns={'id':'customerConcernId',
                             'ro.id':'repairOrderId'},
                             inplace=True)
            
            return repairOrders_customerConcerns_df
        else:
            return None
    except:
        logging.warning('Script failed at parseJSON_CustConc() function')            




def parseJSON_Jobs(data):

    try:
    
        if len(data('content')) != 0:
            
            jobs_df = json_normalize(data=data('content'))
            
            jobs_df.rename(columns={'id':'jobId'}, inplace=True)
            
            seriesList = (jobs_df('labor'), jobs_df('parts'), jobs_df('fees'),
                          jobs_df('discounts'))
            
            jobs_df = jobs_df.drop(axis=1, columns=('labor','parts','fees','discounts'))
            
            return (seriesList,jobs_df)
        else:
            return (None, None)
    except:
        logging.warning('Script failed at parseJSON_Jobs() function')

def parseJSON_Labor(ser, data):
        
    try:
        laborBool = sum((len(x) for x in ser)) != 0
        
        if laborBool:
        
            jobs_labor_df = json_normalize(record_path=('labor'), 
                                               data=data('content'),
                                               meta='id', 
                                               meta_prefix='job.')
            
            jobs_labor_df.rename(columns={'id':'laborId',
                                          'job.id':'jobId'},
                                    inplace=True)
            
            return jobs_labor_df
        else:
            return None
        
    except:
        logging.warning('Script failed at parseJSON_Labor() function')

def parseJSON_Parts(ser, data):
    
    try:
       
        partsBool = sum((len(x) for x in ser)) != 0
        
        if partsBool:
            
            jobs_parts_df = json_normalize(record_path=('parts'),
                                           data=data('content'),
                                           meta='id',
                                           meta_prefix='job.')

            if 'partType.id' in jobs_parts_df:
                jobs_parts_df.rename(columns={'partType.id':'partTypeId','partType.code':'partTypeCode','partType.name':'partTypeName'},inplace=True)
            else:
                jobs_parts_df = jobs_parts_df.drop(axis=1, columns=('partType'))
            
            jobs_parts_df.rename(columns={'id':'partId','job.id':'jobId'}, inplace=True)
            
            return jobs_parts_df
            
        else:
            return None
    except:
        logging.warning('Script failed at parseJSON_Parts() function')

def parseJSON_JobsFees(ser, data):
    
    try:
    
        feesBool_jobs = sum((len(x) for x in ser)) != 0
        
        if feesBool_jobs:

            jobs_fees_df = json_normalize(record_path=('fees'), 
                                              data=data('content'),
                                              meta='id', 
                                              meta_prefix='job.')
            
            jobs_fees_df.rename(columns={'id':'feeId',
                                          'job.id':'parentId'},
                                inplace=True)
            
            jobs_fees_df('parentType') = 'JOB'
            
            return jobs_fees_df
            
        else:
            return None
    except:
        logging.warning('Script failed at parseJSON_JobsFees() function')

def parseJSON_JobsDisc(ser, data):    
    
    try:
    
        discBool_jobs = sum((len(x) for x in ser)) != 0
        
        if discBool_jobs:
        
            jobs_discounts_df = json_normalize(record_path=('discounts'),
                                               data=data('content'),
                                               meta='id',
                                               meta_prefix='job.')
            
            jobs_discounts_df.rename(columns={'id':'discountId',
                                              'job.id':'parentId'},
                                    inplace=True)
            
            jobs_discounts_df('parentType') = 'JOB'
            
            return jobs_discounts_df
            
        else:
            return None
    except:
        logging.warning('Script failed at parseJSON_JobsDisc() function')
    
        
def parseJSON_Customers(data): 
    
    try:
        if len(data('content')) != 0:
            customers_df = json_normalize(data=data('content'))
            customers_df = customers_df.drop(axis=1, 
                                             columns=('address.address1',
                                                      'address.id',
                                                      'address.address2',
                                                      'address.fullAddress',
                                                      'address.streetAddress',
                                                      'contactFirstName',
                                                      'contactLastName',
                                                      'customerType.code',
                                                      'email','notes',
                                                      'okForMarketing'))
            
            phone_df = json_normalize(record_path=('phone'),
                                           data=data('content'),
                                           meta='id',
                                           meta_prefix='phone.')
            
            if phone_df.shape(0) == 0:
                phone_df = None
            else:
                phone_df.rename(columns={'number':'phoneNumber',
                                         'type':'phoneType',
                                         'phone.id':'phoneId',
                                         'id':'customerId'},inplace=True)
                
                
            
            customers_df.rename(columns={'id':'customerId',
                                          'address.city':'city',
                                          'address.zip':'zip',
                                          'customerType.id':'customerTypeId',
                                          'customerType.name':'customerTypeName',
                                          'address.state':'state'},
                                inplace=True)
            
            return (customers_df, phone_df)
        
        else:
            return None
    except:
        logging.warning('Script failed at parseJSON_Customers() function')

def parseJSON_Appointments(data): 
    
    try:
    
        if len(data('content')) != 0:
            appointments_df = json_normalize(data=data('content'))
            
            appointments_df.rename(columns={'id':'appointmentId',
                                            'vehicleId': 'vehicleId',
                                            'customerId': 'customerId'}, inplace=True)
            
            return appointments_df
            
        else:
            return 
    except:
        logging.warning('Script failed at parseJSON_Appointments() function')
        
    
def parseJSON_Vehicles(data):
    
    try:
    
        if len(data('content')) != 0:
            vehicles_df = json_normalize(data=data('content'))
            
            vehicles_df.rename(columns={'id':'vehicleId'}, 
                                   inplace=True)
            
            return vehicles_df
        else:
            return None
    
    except:
        logging.warning('Script failed at parseJSON_Vehicles() function')

def parseJSON_Employees(data):

    try:
        if len(data('content')) != 0:
            employees_df = json_normalize(data=data('content'))
            
            if 'address' in employees_df:
                employees_df = employees_df.drop(axis=1,columns=('address'))
            else:
                employees_df = employees_df.drop(axis=1,columns=('address.address1',
                                                                  'address.id',
                                                                  'address.address2',
                                                                  'address.fullAddress',
                                                                  'address.streetAddress',
                                                                  'address.city',
                                                                  'address.state',
                                                                  'address.zip'))
            employees_df.rename(columns={'id':'employeeId',
                                         'employeePayType.id': 'employeePayTypeId',
                                         'employeePayType.code': 'employeePayTypeCode',
                                         'employeePayType.name': 'employeePayTypeName',
                                         'employeeRole.id': 'employeeRoleId',
                                         'employeeRole.code': 'employeeRoleCode',
                                         'employeeRole.name': 'employeeRoleName'},inplace=True)
                
            employees_df('shopId') = SHOPID
            
            return employees_df
        else:
            return None
    except Exception as e:
        logging.warning('Script failed at parseJSON_Employees() function    {}'.format(e))

#def getUpdatedRecords(data):
#    updateIds = (data(0)('RepairOrderId'),
#                 data(1)('JobId'),
#                 data(2)('CustomerId'),
#                 data(3)('AppointmentId'),
#                 data(4)('VehicleId')
#                 )
#    updateIds =(tuple(i.values) for i in updateIds)
#    
#    return updateIds
    
def getSqliteColNames(tblName):
    try:
        crsr.execute('PRAGMA table_info({})'.format(tblName))
        rows = crsr.fetchall()
        return rows
        
    except:
        logging.warning('Script failed at getSqliteColNames() function')
    

#def dfsToDB(dfList, conn, timeStamp):
#    
#    # Order must match dataframes list for INSERT execution loop
#    dbObjects = ('RepairOrders',
#                   'Sublets',
#                   'Items',
#                   'Fees',
#                   'Discounts',
#                   'CustomerConcerns',
#                   'Jobs',
#                   'Labor',
#                   'Parts',
#                   'Fees',
#                   'Discounts',
#                   'Customers',
#                   'Appointments',
#                   'Vehicles',
#                   'Employees')
#    
#    for df in dfList:
#        if df is None:
#            pass
#        else:
#            df.loc(:,'InsertTime') = timeStamp
#    
#    logging.info('Loading data into SQLite')
#    for i, df in enumerate(dfList):
#        if df is None:
#            pass
#        else:
#            try:
#                df.to_sql(name=dbObjects(i), 
#                          con=conn, 
#                          if_exists='append', 
#                          index=False)
#                logging.info('Successful load to table: {}'.format(dbObjects(i)))
#            except Exception as e:
#                logging.warning(e)
#                pass
#    
#    conn.commit()
    
        

# Used for the purposes of deleting records
# l = list of series (each series is an array of IDs whose records need deleting)
# sql = sql string template list
def buildSqlStrings(l,sql):
    o = ()
    z = (not x is None for x in l)
    for i,s in enumerate(z):
        if s:
            txt = sql(i).format(tuple(l(i)))
            if len(l(i)) == 1:
                txt = txt.replace(',','')
                o.append(txt)
            else:
                o.append(txt)  
    return o

            
          
#    
#def getUpdatedIDs(dfl):
#    x = ()
#    for i in (0,6,11,12,13):
#        if dfl(i) is None:
#            if i == 0:
#                x = x + (None,None,None,None,None,None)
#            elif i == 6:
#                x = x + (None,None,None,None,None)
#            else:
#                x.append(None)
#        else:
#            
#            # Check to see if createdDate and updatedDate are the same for each
#            # record in the dataframe, return a list of True/False that corresponds
#            # to each records' index in the dataframe
#            bools = dfl(i)('createdDate') != dfl(i)('updatedDate')
#            
#            # Derive IDs for sublets, items, fees, discounts, & customer concens
#            # that are linked to each repairOrderID
#            if i == 0:
#                roIDs = dfl(i).loc(bools,'RepairOrderId')
#                x.append(roIDs)
#                
#                if dfl(1) is not None:
#                    subletIDs = dfl(1).loc(dfl(1)('RepairOrderId').isin(roIDs))('SubletId')
#                    x.append(subletIDs)
#                    if dfl(2) is not None:
#                        x.append(dfl(2).loc(dfl(2)('SubletId').isin(subletIDs))('ItemId'))
#                    else:
#                        x.append(None) # for items
#                else:
#                    x.append(None) # for sublets
#                    x.append(None) # for items
#                    
#                if dfl(3) is not None:
#                    x.append(dfl(3).loc(dfl(3)('ParentId').isin(roIDs))('ParentId'))
#                else:
#                    x.append(None)
#                if dfl(4) is not None:    
#                    x.append(dfl(4).loc(dfl(4)('ParentId').isin(roIDs))('ParentId'))
#                else:
#                    x.append(None)
#                if dfl(5) is not None:
#                    x.append(dfl(5).loc(dfl(5)('RepairOrderId').isin(roIDs))('CustomerConcernId'))   
#                else:
#                    x.append(None)
#            
#            # Derive IDs for labor, parts, fees, & discounts that are linked
#            # to each JobID
#            elif i == 6:
#                jobIDs = dfl(i).loc(bools,'JobId')
#                x.append(jobIDs)
#                if dfl(7) is not None:
#                    x.append(dfl(7).loc(dfl(7)('LaborId').isin(jobIDs))('LaborId'))
#                else:
#                    x.append(None) 
#                if dfl(8) is not None:
#                    x.append(dfl(8).loc(dfl(8)('PartId').isin(jobIDs))('PartId'))
#                else:
#                    x.append(None)
#                if dfl(9) is not None:
#                    x.append(dfl(9).loc(dfl(9)('ParentId').isin(jobIDs))('ParentId'))
#                else:
#                    x.append(None)
#                if dfl(10) is not None:
#                    x.append(dfl(10).loc(dfl(10)('ParentId').isin(jobIDs))('ParentId'))
#                else:
#                    x.append(None)
#                
#            elif i == 11:
#                x.append(dfl(i).loc(bools,'CustomerId'))
#            elif i == 12:
#                x.append(dfl(i).loc(bools,'AppointmentId'))
#            elif i == 13:
#                x.append(dfl(i).loc(bools,'VehicleId'))    
#    return x


# dfList = list of 14 dataframes (one for each table in sql)
# ids = list of 14 series (returned by getUpdatedIDs() function)
# pks =     
#def splitDF(dfList,ids,pks):
#    uDFs = ()
#    nDFs = ()
#    for i, df in enumerate(dfList):
#        if df is None:
#            uDFs.append(None)
#            nDFs.append(None)
#        else:
#            tDF = df.loc(df(pks(i)).isin(ids(i)), :)
#            uDFs.append(tDF)
#            df.drop(tDF.index, inplace=True)
#            nDFs.append(df)
#    return (uDFs,nDFs)

        
DELETE_SQL_STMTS = (

                '''
                DELETE
                FROM RepairOrders
                WHERE RepairOrderId IN {}

                ''',


                '''
                DELETE
                FROM Sublets
                WHERE RepairOrderId IN {}
                ''',


                '''
                DELETE
                FROM Items
                WHERE SubletId IN
                (SELECT SubletID FROM Sublets WHERE RepairOrderId IN {})
                ''',


                '''
                DELETE
                FROM Fees
                WHERE ParentId IN {}
                ''',


                '''
                DELETE
                FROM Discounts
                WHERE ParentID IN {}
                ''',


                '''
                DELETE
                FROM CustomerConcerns
                WHERE RepairOrderId IN {}
                ''',


                '''
                DELETE
                FROM Jobs
                WHERE JobId IN {}
                ''',


                '''
                DELETE
                FROM Labor
                WHERE JobId IN {}
                ''',


                '''
                DELETE
                FROM Parts
                WHERE JobId IN {}
                ''',


                '''
                DELETE
                FROM Fees
                WHERE ParentId IN {}
                ''',


                '''
                DELETE
                FROM Discounts
                WHERE ParentID IN {}
                ''',


                '''
                DELETE
                FROM Customers
                WHERE CustomerId IN {}
                ''',
                
                
                '''
                DELETE
                FROM Phone
                WHERE CustomerId IN {}
                ''',


                '''
                DELETE
                FROM Appointments
                WHERE AppointmentId IN {}
                ''',


                '''
                DELETE
                FROM Vehicles
                WHERE VehicleID IN {}
                ''',


                '''
                DELETE
                FROM Employees
                WHERE EmployeeID IN {}
                '''


                )
                


# Output: List with 6 dataframes

def buildRoDFs():
    
    try:
    
        logging.info('buildRODFs() function involked with ro_pgs parameter set to {}'.format(ro_pgs))
      # build dataframes 1-6 inside roDFs list
        for p in range(ro_pgs):
            params1 = {'shop':SHOPID,
                       'size':pgSize,
                       'page':p,
                       'updatedDateStart':prevCallTime,
                       'updatedDateEnd':thisCallTime
                      }
            
            logging.info('Initiating Call {} of {}'.format(p + 1, ro_pgs))
            r = singleCall(params1, token, 'repair-orders').json()
            
            ro_tup = parseJSON_RO(r) # r is a dictionary and parseJSON_RO accesses the ('content') key where results are stored
            
            # ro_tup(0) is the list of series derived from the repairOrders_df
            # These series are passed into the dataframe constructors for the 
            # purpose of abandandoning the creation of the dataframe if no data
            # exists for each of the columns: Sublets, Fees, Discounts, CustConcerns
            # The functions will return NoneType objects if no data is detected
            subletsItemsTup = parseJSON_SubletsItems(ro_tup(0)(0),r)
            fees_df = parseJSON_ROFees(ro_tup(0)(1),r)
            disc_df = parseJSON_RODisc(ro_tup(0)(2),r)
            custConc_df = parseJSON_CustConc(ro_tup(0)(3), r)
            
            # on the first iteration, establish the list that will hold the original
            # RO dataframe plus the 5 derived frames (Sublets, Items, Fees, Discounts,
            # and CustomerConcerns) - Items is further derived from sublets
            if p == 0:
                roDFs = (ro_tup(1),subletsItemsTup(0),subletsItemsTup(1),fees_df,disc_df,custConc_df)
            
            # if its not the first loop, append the results from the call to the 
            # existing dataframes inside the appropriate roDFs element
            else:
                
                # no need to check for None here, there should always be data
                # due to the fact that were in a loop for parsing confirmed returned
                # results for repair orders. All other df's are derived, not this
                roDFs(0) = pandas.concat((roDFs(0),ro_tup(1)),ignore_index=True, sort=True)
                
                # results for these may not show up until a later iteration, in which
                # case the element in roDFs might have previously been a None object
                # if this is the case, skip the append and add the df for the first 
                # time so that the next pass has something to append to
                if roDFs(1) is not None:
                    # in the case that the current iteration returns a None object rather 
                    # than additional data, skip appending the None obj to avoid error
                    if subletsItemsTup(0) is not None:
                        pandas.concat((roDFs(1),subletsItemsTup(0)),ignore_index=True, sort=True)
                else:
                    roDFs(1) = subletsItemsTup(0)
                    
                if roDFs(2) is not None:
                    if subletsItemsTup(1) is not None:
                        roDFs(2) = pandas.concat((roDFs(2),subletsItemsTup(1)),ignore_index=True, sort=True)
                else:    
                    roDFs(2) = subletsItemsTup(1)
                    
                if roDFs(3) is not None:
                    if fees_df is not None:
                        roDFs(3) = pandas.concat((roDFs(3),fees_df),ignore_index=True, sort=True)
                else:    
                    roDFs(3) = fees_df
                    
                if roDFs(4) is not None:
                    if disc_df is not None:
                        roDFs(4) = pandas.concat((roDFs(4),disc_df),ignore_index=True, sort=True)
                else:
                    roDFs(4) = disc_df
                
                if roDFs(5) is not None:
                    if custConc_df is not None:
                        roDFs(5) = pandas.concat((roDFs(5),custConc_df),ignore_index=True, sort=True)
                else:
                    roDFs(5) = custConc_df
                
        return roDFs
    
    except:
        logging.warning('Script failed at buildRoDFs() function')


        
    
def buildJobsDFs():
    
    try:
    
        logging.info('buildJobsDFs() function involked with jobs_pgs parameter set to {}'.format(jobs_pgs))
        
        for p in range(jobs_pgs):
    
            params1 = {'shop':SHOPID,
                       'size':pgSize,
                       'page':p,
                       'updatedDateStart':prevCallTime,
                       'updatedDateEnd':thisCallTime
                      }
            
            logging.info('Initiating Call {} of {}'.format(p + 1, jobs_pgs))
            r = singleCall(params1, token, 'jobs').json()
               
            job_tup = parseJSON_Jobs(r)
            
            labor_df = parseJSON_Labor(job_tup(0)(0), r)      
            parts_df = parseJSON_Parts(job_tup(0)(1), r)    
            fees_df = parseJSON_JobsFees(job_tup(0)(2), r)   
            disc_df = parseJSON_JobsDisc(job_tup(0)(3), r)
            
            if p == 0:
                jobsDFs = (job_tup(1), labor_df, parts_df, fees_df, disc_df)
            else:
                
                jobsDFs(0) = pandas.concat((jobsDFs(0),job_tup(1)),ignore_index=True, sort=True)
            
                if jobsDFs(1) is not None:
                    if labor_df is not None:
                        jobsDFs(1) = pandas.concat((jobsDFs(1), labor_df),ignore_index=True, sort=True)
                else:
                    jobsDFs(1) = labor_df
        
                if jobsDFs(2) is not None:
                    if parts_df is not None:
                        jobsDFs(2) = pandas.concat((jobsDFs(2), parts_df),ignore_index=True, sort=True)
                else:
                    jobsDFs(2) = parts_df
                        
                if jobsDFs(3) is not None:
                    if fees_df is not None:
                        jobsDFs(3) = pandas.concat((jobsDFs(3), fees_df),ignore_index=True, sort=True)
                else:
                    jobsDFs(3) = fees_df
                    
                if jobsDFs(4) is not None:
                    if disc_df is not None:
                        jobsDFs(4) = pandas.concat((jobsDFs(4), disc_df),ignore_index=True, sort=True)
                else:
                    jobsDFs(4) = disc_df    
        
        return jobsDFs
    
    except:
        logging.warning('Script failed at buildJobsDFs() function')

    

def buildCustomerDF():
    
    try:
    
        logging.info('buildCustomersDFs() function involked with customers_pgs parameter set to {}'.format(customers_pgs))
        
        for p in range(customers_pgs):
            params1 = {'shop':SHOPID,
                       'size':pgSize,
                       'page':p,
                       'updatedDateStart':prevCallTime,
                       'updatedDateEnd':thisCallTime
                      }
            
            logging.info('Initiating Call {} of {}'.format(p + 1, customers_pgs))
            r = singleCall(params1, token, 'customers').json()
            
            
            if p == 0:
                cust_phone_df_tuple = parseJSON_Customers(r)
                cust_df = cust_phone_df_tuple(0)
                phone_df = cust_phone_df_tuple(1)
                
            else:
                
                cust_phone_df_tuple_temp = parseJSON_Customers(r)
                cust_df_temp = cust_phone_df_tuple_temp(0)
                phone_df_temp = cust_phone_df_tuple_temp(1)
                
                cust_df = pandas.concat((cust_df,cust_df_temp), ignore_index=True, sort=True)
                phone_df = pandas.concat((phone_df,phone_df_temp), ignore_index=True, sort=True)
        
        return (cust_df, phone_df)

    except:
        logging.warning('Script failed at buildCustomerDF() function')

def buildAppointmentsDF():
    
    try:
    
        logging.info('buildAppointmentsDFs() function involked with appointments_pgs parameter set to {}'.format(appointments_pgs))
        
        for p in range(appointments_pgs):
    
            params1 = {'shop':SHOPID,
                       'size':pgSize,
                       'page':p,
                       'updatedDateStart':prevCallTime,
                       'updatedDateEnd':thisCallTime
                      }
            
            logging.info('Initiating Call {} of {}'.format(p + 1, appointments_pgs))
            r = singleCall(params1, token, 'appointments').json()
            
            if p == 0:
                appointments_df = parseJSON_Appointments(r)
            else:
                appointments_df_temp = parseJSON_Appointments(r)
                appointments_df = pandas.concat((appointments_df,appointments_df_temp), ignore_index=True, sort=True)
        return appointments_df
    
    except:
        logging.warning('Script failed at buildAppointmentsDF() function')


def buildVehiclesDF():
    
    try:
        
        logging.info('buildVehiclesDFs() function involked with vehicles_pgs parameter set to {}'.format(vehicles_pgs))
        
        for p in range(vehicles_pgs):
    
            params1 = {'shop':SHOPID,
                       'size':pgSize,
                       'page':p,
                       'updatedDateStart':prevCallTime,
                       'updatedDateEnd':thisCallTime
                      }
            
            logging.info('Initiating Call {} of {}'.format(p + 1, vehicles_pgs))
            r = singleCall(params1, token, 'vehicles').json()
            
            if p == 0:
                vehicles_df = parseJSON_Vehicles(r)
            else:
                vehicles_df_temp = parseJSON_Vehicles(r)
                vehicles_df = pandas.concat((vehicles_df,vehicles_df_temp), ignore_index=True, sort=True)
                
        return vehicles_df
    
    except:
        logging.warning('Script failed at buildVehiclesDF() function')

def buildEmployeesDF():

    try:
        
        logging.info('buildEmployeesDFs() function involked with employees_pgs parameter set to {}'.format(employees_pgs))
    
        for p in range(employees_pgs):
    
            params1 = {'shop':SHOPID,
                       'size':pgSize,
                       'page':p,
                       'updatedDateStart':prevCallTime,
                       'updatedDateEnd':thisCallTime
                      }
    
            logging.info('Initiating Call {} of {}'.format(p + 1, employees_pgs))
            r = singleCall(params1, token, 'employees').json()
    
            if p == 0:
                employees_df = parseJSON_Employees(r)
            else:
                employees_df_temp = parseJSON_Employees(r)
                employees_df = pandas.concat((employees_df,employees_df_temp), ignore_index=True, sort=True)
    
        return employees_df

    except:
        logging.warning('Script failed at buildEmployeesDF() function')


if __name__ == '__main__':
    
  
    
    ENVIRON = int(sys.argv(2))
   
    if ENVIRON == 1:
        
        logging.basicConfig(filename='C:\tekmetric etl\configs\runlog2.txt',
                format='%(asctime)s    %(levelname)s:    %(message)s',
                datefmt='%m/%d/%Y %I:%M:%S %p',
                level=logging.INFO)
        
            
        SHOPID = sys.argv(1)
        DB_PATH = 'E:\ServerFolders\DB2\Databases\TekMetric_API_Stage{}.db'
        CREDS_FILE = 'C:\Tekmetric ETL\Configs\TekMetricAPIcreds.json'
        CALLTIME_FILE = 'C:\Tekmetric ETL\Configs\callTime_{}.json'.format(SHOPID)
        
        logging.info('Beginning execution for store {}'.format(SHOPID))
            
    else:
     
        logging.basicConfig(filename='C:\Users\Brian-Admin\Documents\Tekmetric API Configs\runlog2.txt',
                            format='%(asctime)s    %(levelname)s:    %(message)s',
                            datefmt='%m/%d/%Y %I:%M:%S %p',
                            level=logging.INFO) 
        

            
        SHOPID = sys.argv(1)
        DB_PATH = 'E:\ServerFolders\DB2\Databases\TekMetric_API_Stage{}.db'
        CREDS_FILE = 'C:\Users\Brian-Admin\Documents\Tekmetric API Configs\TekMetricAPIcreds.json'
        CALLTIME_FILE = 'C:\Users\Brian-Admin\Documents\Tekmetric API Configs\callTime_{}.json'.format(SHOPID)
        
        logging.info('Beginning execution for store {}'.format(SHOPID))


    PKS = ('RepairOrderId',
           'SubletId',
           'ItemId',
           'ParentId',
           'ParentId',
           'CustomerConcernId',
           'JobId',
           'LaborId',
           'PartId',
           'ParentId',
           'ParentId',
           'CustomerId',
           'PhoneId',
           'AppointmentId',
           'VehicleId',
           'EmployeeId')
    
    # might not use this going forward, starting to move towards StoreID approach
    # as of addition of sys.argv on 10.23.19
    stores = ('Suwanee')
    
    runType = ('PROD','DEV')
    
    # move down if connection creates mem overhead
    conn = sqlite3.connect(DB_PATH.format('_' + runType(int(sys.argv(3)))))
    crsr = conn.cursor()
    
    prevCallTime = getPrevCallTime(CALLTIME_FILE)
    thisCallTime = time2str(datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'))
    
    logging.info('Retrieving data from {} to {}'.format(prevCallTime,thisCallTime))
        
    with open(CREDS_FILE,'r') as x:
        creds = json.load(x)
    
    authResp = getBearerAndScope(stores(0))
    token = authResp(0) # Bearer Auth token
    scope = authResp(1).split() # list of storeIDs, IDs will be in str format
    
    # per request of tekmetric, keep at 100 per call
    pgSize = 100
    

    # these params are only used to gauge the size of the result set that will
    # ultimately be returned given the time and store parameters
    # this set of parameters is fed into the getdata() function with makes
    # successive calls to each of the endpoints were hitting
    # Well use the 'totalPages' metadata attribute to store in a variable
    # that will determine how many times the "real" go-get-all-the-data functions
    # will need to call the endpoint to exhaust all the records that fall within
    # the call parameters. Those variables are the {}_pgs below
    params_r = {'shop':SHOPID,
               'size':pgSize,
               'page':0,
               'updatedDateStart':prevCallTime,
               'updatedDateEnd':thisCallTime
               }
    params_j = {'shop':SHOPID,
               'size':pgSize,
               'page':0,
               'updatedDateStart':prevCallTime,
               'updatedDateEnd':thisCallTime
               }
    params_c = {'shop':SHOPID,
               'size':pgSize,
               'page':0,
               'updatedDateStart':prevCallTime,
               'updatedDateEnd':thisCallTime
               }
    params_a = {'shop':SHOPID,
               'size':pgSize,
               'page':0,
               'updatedDateStart':prevCallTime,
               'updatedDateEnd':thisCallTime
               }
    params_v = {'shop':SHOPID,
               'size':pgSize,
               'page':0,
               'updatedDateStart':prevCallTime,
               'updatedDateEnd':thisCallTime
               }    
    params_e = {'shop':SHOPID,
               'size':pgSize,
               'page':0,
               'updatedDateStart':prevCallTime,
               'updatedDateEnd':thisCallTime
               } 
    # Get metadata from API with a single call to each endpoint before
    # doing the heavy lifting

    # Repair Orders
    resp_r = singleCall(params_r,token,'repair-orders')
    d_r = resp_r.json() #dictionary
    ro_elms = d_r('totalElements')
    ro_pgs = d_r('totalPages')
    ro_empty = d_r('empty')
    logging.info('{} repair orders found'.format(ro_elms))
    
    # Jobs
    resp_j = singleCall(params_j,token,'jobs')
    d_j = resp_j.json() #dictionary
    jobs_elms = d_j('totalElements')
    jobs_pgs = d_j('totalPages')
    jobs_empty = d_j('empty') 
    logging.info('{} jobs found'.format(jobs_elms))
    
    # Customers
    resp_c = singleCall(params_c,token,'customers')
    d_c = resp_c.json() #dictionary
    cust_elms = d_c('totalElements')
    customers_pgs = d_c('totalPages')
    cust_empty = d_c('empty') 
    logging.info('{} customers found'.format(cust_elms))

    # Appointments
    resp_a = singleCall(params_a,token,'appointments')
    d_a = resp_a.json() #dictionary
    appts_elms = d_a('totalElements')
    appointments_pgs = d_a('totalPages')
    appts_empty = d_a('empty') 
    logging.info('{} appointments found'.format(appts_elms))

    # Vehicles
    resp_v = singleCall(params_v,token,'vehicles')
    d_v = resp_v.json() #dictionary
    vehicles_elms = d_v('totalElements')
    vehicles_pgs = d_v('totalPages')
    vehicles_empty = d_v('empty') 
    logging.info('{} vehicles found'.format(vehicles_elms))
    
    # Employees
    resp_e = singleCall(params_v,token,'employees')
    d_e = resp_e.json() #dictionary
    employees_elms = d_e('totalElements')
    employees_pgs = d_e('totalPages')
    employees_empty = d_e('empty')
    logging.info('{} employees found'.format(employees_elms))    
    
    # Extra layer of checks to abandon the creation of ro and job-based dataframes
    if ro_empty:
        roDFs = (None,None,None,None,None,None)
    else:
        roDFs = buildRoDFs() 
        
    if jobs_empty:
        jobsDFs = (None,None,None,None,None)
    else:
        jobsDFs = buildJobsDFs()
        
    if cust_empty:
        customers_df = None
        cust_phone_dfs = (None,None)
    else:
        cust_phone_dfs  = buildCustomerDF() 
        
    if appts_empty:
        appointments_df = None
    else:
        appointments_df = buildAppointmentsDF()
        
    if vehicles_empty:
        vehicles_df = None
    else:
        vehicles_df = buildVehiclesDF()

    if employees_empty:
        employees_df = None
    else:
        employees_df = buildEmployeesDF()
        
        
    # allDFs, when all is said and done here, should have 14 elements: each one a 
    # dataframe that corresponds to a sql table. Order is important here, because
    # this list will be fed to the getUpdatedIDs() function, which relies on
    # calling the index of each df in order to perform specific tasks specially
    # coded to cater to the unique columns in each dataframe (those dataframes
    # were crafted from the json response - using json_normalize due to the nested
    # nature of some of the attributes - to exactly match a sql table)
    
    # the first 6 dfs (provided via the roDFs list) are related to the 
    # repair-order endpoint response. The first is the adjusted repairOrders_df 
    # itself, stripped of the columns that are used to create the other 5 
    # dataframes: (Subets, Items, Fees, Discounts, and CustomerConerns)
    
    # the jobsDFs list provides 5 more dataframes  - same concept as above
    # They are: Jobs, Labor, Parts, Fees, Discounts
    # Fees and discounts for ROs and Jobs end up going to the same sql table
    allDFs = roDFs + jobsDFs
    allDFs.append(cust_phone_dfs(0))
    allDFs.append(cust_phone_dfs(1))
    allDFs.append(appointments_df)
    allDFs.append(vehicles_df)
    allDFs.append(employees_df)
    
    if allDFs(0) is not None:
        ros = allDFs(0).loc(allDFs(0)('createdDate') != allDFs(0)('updatedDate'),'repairOrderId')
    else:
        ros = None
    if allDFs(6) is not None:    
        jobs = allDFs(6).loc(allDFs(6)('createdDate') != allDFs(6)('updatedDate'),'jobId')
    else:
        jobs= None
    if allDFs(11) is not None:
        custs = allDFs(11).loc(allDFs(11)('createdDate') != allDFs(11)('updatedDate'),'customerId')
    else:
        custs = None    
    if allDFs(13) is not None:
        appts = allDFs(13).loc(allDFs(13)('createdDate') != allDFs(13)('updatedDate'),'appointmentId')
    else:
        appts = None
    if allDFs(14) is not None:
        vehic = allDFs(14).loc(allDFs(14)('createdDate') != allDFs(14)('updatedDate'),'vehicleId')
    else:
        vehic = None
    if allDFs(15) is not None:
        emplo = allDFs(15).loc(allDFs(15)('createdDate') != allDFs(15)('updatedDate'),'employeeId')
    else:
        emplo = None
    
    uIDs = (ros,ros,ros,ros,ros,ros,jobs,jobs,jobs,jobs,jobs,custs,custs,appts,vehic,emplo)
    


    
    # Passing the IDs whose records need to be updated into a sql string that will
    # first be used to delete the record entirely before later replacing it.
    # this is necessary becuase the granulaity is not fine enough to know which
    # attribute of a record was updated in tekmetric's system. We only know that
    # 'something' was updated, so we have to rip & replace everything
    # that includes circumstances where a record (such as a CustomerConcern) is
    # updated. Since CustomerConcern records are embedded in the repair-orders
    # response, an update to CustomerConcens will trigger a new updatedDate at 
    # the repair-order level. We have no way of knowing that this specific event
    # happened or if a new fee was added. Becuase of that, we must rip and replace 
    # all child records in every table associated with that repairOrderID.
    # Something as simple as updating a note in customer concerns would cause us
    # to have to delete and rewrite every fee, discount, sublet, and item in 
    # our data warehouse. the getUpdatedIDs function handles this detection

    sqlUpdates = buildSqlStrings(uIDs,DELETE_SQL_STMTS)

    for sql in sqlUpdates:
        crsr.execute(sql)
   
    dbObjects = ('RepairOrders',
                   'Sublets',
                   'Items',
                   'Fees',
                   'Discounts',
                   'CustomerConcerns',
                   'Jobs',
                   'Labor',
                   'Parts',
                   'Fees',
                   'Discounts',
                   'Customers',
                   'Phone',
                   'Appointments',
                   'Vehicles',
                   'Employees')
    
    insert_success = True
    for i, df in enumerate(allDFs):
        try:
            if df is None:
                pass
            else:
                cols = ()
                crsr.execute('PRAGMA table_info({})'.format(dbObjects(i)))
                result = crsr.fetchall()
                for r in result:
                    cols.append(r(1))
                df.loc(:,'insertTime') = thisCallTime
                df = df(cols)
                data = (tuple(x) for x in df.values)
                qString = ''
                for j in range(df.shape(1)-1):
                    qString = qString + ', ?'
                conn.executemany('INSERT INTO {} VALUES (?{})'.format(dbObjects(i),qString),data)
                logging.info('{} record(s) loaded to {} table'.format(df.shape(0),dbObjects(i)))
        except:
            logging.warning('Failure at load to {} table'.format(dbObjects(i)))
            conn.rollback()
            insert_success = False
            break
    
    if insert_success:
        conn.commit()
        writeThisCallTime(CALLTIME_FILE,thisCallTime)
        
        
    logging.info('Store {}: completed at '.format(SHOPID) + thisCallTime)
     
```

Probelm with implementing a method inside of another method. Python OOP

This is my code

 class GameDictionary(object):
        def __init__(self):
            pygame.init()
            game_display.fill(white)
            self.game_display = pygame.display.set_mode((window_width,window_height))
            self.fontBig  = pygame.font.Font('HanaMinA.ttf', 400)
            self.fontMedium = pygame.font.Font('HanaMinA.ttf', 75)
            self.fontSmall   = pygame.font.Font('HanaMinA.ttf', 50)
            self.fontFurigana = pygame.font.Font('HanaMinA.ttf', 25)
            self.kanji = 0
            self.listaKanjisInterna = ListaDeKanjis.copy() #Copies global list and uses that one instead
            self.traduccion = False

    def test(self,variable):
        print(str(self.kanji) + str(variable))
    def run(self):
        GameDictionary.test(**GameDictionary()**,"TEXT")

Why does it make me call GameDictionary() inside the run method?

Without it, it doesnt work

c++11 – Grade Calculator (New OOP Version from one previous)

I’m trying to develop an OOP way of thinking. I was hoping someone would be kind enough to spare some of their valuable time to review my Grade Calculator OOP program. As always, I’d like to know what I’ve done well, what I should improve upon, and any suggestions on how I could perhaps improve what I have? By the way, I have a class called Class. I probably should prefix it with “cls” so as not to confuse. Treat this program as is supposed to be entered, I haven’t error checked it. The point of this program is to develop in OOP.

   // Task 1.cpp : This file contains the 'main' function. Program execution begins and ends there.


#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <numeric>
#include <string>
class TestPaper
{
public:
    int m_scoreOutOf;  
    bool checkBoundary(int value, int boundary) {
        if (value < 0 || value > boundary) {
            std::cout << "Score must be between " << " 0 and " << boundary << ". Please try again.n";
            return false;
        }
        return true;
    }

};
class Student {
private:
    std::string m_name;
    int m_scoreGot;
public:
   
    Student(std::string name, int scoreGot)
        :m_name(name), m_scoreGot(scoreGot){}

    std::string getName() const { return m_name; }
    int getScoreGot() const { return m_scoreGot; }
};

class Class {
private:
    std::vector<Student>students;
public:
    void AddStudent(TestPaper& testPaper) {
        std::string name = "";
        int scoreGot = 0;
        std::cout << "Enter student name: ";
        std::getline(std::cin >> std::ws, name);
        do
        {
            std::cout << "nWhat did " << name << " score?nEnter a score between 0 and "
                << testPaper.m_scoreOutOf << ": ";
            std::cin >> scoreGot;
        } while (testPaper.checkBoundary(scoreGot, testPaper.m_scoreOutOf) == false);
        students.push_back({ name, scoreGot });
    }

    std::vector<Student>& accessStudents() { return students; }
};

class GradeCalculator {
    TestPaper m_testPaper;
    Class m_ClassOfStudents;
public:
    GradeCalculator(TestPaper testPaper, Class classOfStudents) :m_testPaper(testPaper), m_ClassOfStudents(classOfStudents) {}
    void DisplayMenu() {

        std::cout << "n1. Add student and their graden";
        std::cout << "2. Calculate class scoren";
        std::cout << "3. Modify testpaper (haven't implemented this yet)n";
    }
    

    double averageGrade() {
        auto sum = std::transform_reduce(m_ClassOfStudents.accessStudents().begin(), m_ClassOfStudents.accessStudents().end(), 0.0, std::plus<>(),
            (&)(auto& student) { return calculateGradePercentage(student); });
        return sum / m_ClassOfStudents.accessStudents().size();
    }
    double calculateGradePercentage(Student &student)
    {
        return static_cast<double>(student.getScoreGot()) / static_cast<double>(m_testPaper.m_scoreOutOf) * 100;
    }
    void DisplayResult() {
        for (auto& student : m_ClassOfStudents.accessStudents()) {
            std::cout << "Percentage scores are: n";
            std::cout << student.getName() << ": " << calculateGradePercentage(student) << "%n";
        }
        std::cout << "Average grade perecentage: " << averageGrade() << "%n";
    }
    void runProgram() {

        int menuChoice = 0;
        while (true)
        {
            DisplayMenu();
            std::cout << "nEnter a choice from the menu: ";
            std::cin >> menuChoice;
            switch (menuChoice)
            {
            case 1:
                m_ClassOfStudents.AddStudent(m_testPaper);
                break;
            case 2:
                DisplayResult();
                break;
            default:
                std::cout << "Invalid choice!n";
            }
        }
    }
};

int main()
{
    TestPaper testPaper({ 20 });
    Class classOfStudents;
    GradeCalculator calculator(testPaper, classOfStudents);
    calculator.runProgram();
   
}

design – What OOP pattern to use for an ordered list of unrelated events

This is a bit of an invented example but I think it best illustrates my question: Say I’m creating a chess replay event API. Say I have a lot of different “events” I want to keep track of, in a specified order. Here might be some examples:

  1. A move event — this contains the previous and new square.

  2. A timer event — this contains the timestamp that the timer was toggled between players

  3. A chat message event — this contains the player ID, the message and time sent

…etc. The point is that the data model for each event is very different — there isn’t much of a common interface.

I want to design an API that can store and expose essentially a List<Event> to a client who can choose to process these different events as they wish. We don’t know what clients will do with this information: Perhaps one client may need to do text analysis on the ChatMessageEvents, and one may consume and replays these events in the UI. The challenge is that ordering between events must be preserved, so I can’t separate by methods like getMoveEvents and getTimerEvents since a TimerEvent can happen between move events and the client may need that information.

I could expose a visitor to allow clients to handle each event type differently in the list, but I’m wondering if there’s a better pattern to handle a situation like this.

Tic Tac Toe (New OOP Design)

Would someone be able to review my latest OOP design of my Tic Tac Toe / Noughts & Crosses program? I’d like to know what I’ve done well and whether I could improve the OOP Design. Many thanks in advance.

This is a follow-up to this post.

    #include <iostream>
    #include <vector>
    #include <ctime>
    #include <cstdlib>
    #include <map>
    #include <algorithm>
    class Player {
    public:
        unsigned char m_symbol;
        const std::string m_type;
        int m_wins;
        int m_draws;
        virtual int nextMove() const = 0;
        Player(const unsigned char symbol, std::string&& type)
            :m_symbol{ symbol }, m_type{ type }, m_wins{ 0 }, m_draws{ 0 }{}
    };
    class Human : public Player {
    public:
        Human(unsigned char symbol) :Player{ symbol, "Human" } {}
        virtual int nextMove() const override {
            int move;
            std::cout << "Enter a number on the board (e.g. 1): ";
            std::cin >> move;
            return move;
        }
    };
    class Robot : public Player {
    public:
        Robot(unsigned char symbol) :Player{ symbol, "Robot" } {}
        virtual int nextMove() const override {       
            int randNum = 0;
            std::srand(std::time(0));   
            randNum = rand() % 9 + 1;
            return randNum;
        }
    };
    class Game
    {
        Player* one;
        Player* two;
        Player* turn;
        Player* winner;
        std::map<int, unsigned char>board;

    bool isMoveValid(const int move) {
        return std::find_if(board.begin(), board.end(), (&)(auto pair) {
            return pair.first == move && pair.second == '-';
            }) != board.end();
    }
    void performMove(const int move)
    {          
        board.at(move) = turn->m_symbol;
    }
    
    void playerMove() 
    {    
        int move = 0;
        
        if (turn->m_type == "Human") 
        {
            do
            {
                move = turn->nextMove();
                if (isMoveValid(move) == false) 
                {
                    std::cout << "Invalid move!n";
                }
            }
            while (isMoveValid(move) == false);
        }
        else 
        {
            std::vector<int>numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            do
            {
                move = turn->nextMove();
                if (isMoveValid(move) == false) 
                {
                    std::remove_if(numbers.begin(), numbers.end(), (&)(auto &number) {
                        return number == move;
                    });
                }
            }
            while (isMoveValid(move) == false);
        }
        performMove(move);
    }
    void switchPlayers() 
    {
        turn = turn == one ? two : one;
    }
    bool win() {
        if (board.at(1) == turn->m_symbol && board.at(2) == turn->m_symbol && board.at(3) == turn->m_symbol) {
            winner = turn;         
            return true;
        }
        else if (board.at(4) == turn->m_symbol && board.at(5) == turn->m_symbol && board.at(6) == turn->m_symbol) {
            winner = turn;    
            return true;
        }
        else if (board.at(7) == turn->m_symbol && board.at(8) == turn->m_symbol && board.at(9) == turn->m_symbol) {
            winner = turn;
            return true;
        }
        else if (board.at(1) == turn->m_symbol && board.at(4) == turn->m_symbol && board.at(7) == turn->m_symbol) {
            winner = turn;           
            return true;
        }
        else if (board.at(2) == turn->m_symbol && board.at(5) == turn->m_symbol && board.at(8) == turn->m_symbol) {
            winner = turn;             
            return true;
        }
        else if (board.at(3) == turn->m_symbol && board.at(6) == turn->m_symbol && board.at(9) == turn->m_symbol) {
            winner = turn;        
            return true;
        }
        else if (board.at(1) == turn->m_symbol && board.at(5) == turn->m_symbol && board.at(9) == turn->m_symbol) {
            winner = turn;    
            return true;
        }
        else if (board.at(7) == turn->m_symbol && board.at(5) == turn->m_symbol && board.at(3) == turn->m_symbol) {
            winner = turn;       
            return true;
        }
        else
        {
            return false;
        }
    }
    bool draw() {
        return std::all_of(board.begin(), board.end(), (&)(auto& pair) {return pair.second != '-'; });
    }
    void ResetBoard() {
        std::for_each(board.begin(), board.end(), (&)(auto& pair) {
            pair.second = '-';
            });
    }
public:

    Game(Player& one, Player& two)
        :one(&one), two(&two), turn(&one),winner(nullptr)
    {
        board = 
        { std::make_pair(1,'-'),std::make_pair(2,'-'),std::make_pair(3,'-'),
          std::make_pair(4,'-'),std::make_pair(5,'-'),std::make_pair(6,'-'),
          std::make_pair(7,'-'),std::make_pair(8,'-'),std::make_pair(9,'-') 
        };
    
    }
    
    void DisplayBoard()
    {

        for (auto const& cell : board)
        {
            if (cell.first % 3 == 1) {
                std::cout << "nn";
            }
            if (cell.second != '-') {
                std::cout << cell.second << "        ";
            }
            else
            {
                std::cout << cell.first << "        ";
            }
        }
        std::cout << "nn";
    }
    Player* gameLoop() {

        for (;;) 
        {
            DisplayBoard();
            playerMove();      
            if (win()) 
            {
                std::cout << winner->m_symbol << " is the winner!n";
                break;
            }
            else if (draw()) 
            {
                winner = nullptr;
                break;
            }
            switchPlayers();
        }

        DisplayBoard();
        ResetBoard();
        return winner;
    }
};


int main()
{
    Robot robot1('X');
    Robot robot2('O');

    Game game(robot1, robot2);
    //game.gameLoop();
    std::vector<Robot>player = { robot1, robot2 };
    int round = 3;
    int roundCount = 0;
    Player* winner = nullptr;
    do
    {

        int gameCount = 1;
        int totalGamesinRound = 3;
        std::cout << "START GAME!n";
        system("pause");
        system("cls");
        std::cout << "nROUND " << ++roundCount << ". . .n";
        do
        {
            std::cout << "Game " << gameCount << " of round " << roundCount << "n";
            winner = game.gameLoop();

            if (winner != nullptr)
            {
                std::cout << "Winner of game " << gameCount << " is type: " << winner->m_type << ": " << winner->m_symbol << "n";
                winner->m_wins++;
            }
            else
            {
                system("cls");
                std::cout << "Game " << gameCount << " is a draw!n";
                
            }

            gameCount++;
            totalGamesinRound--;
        } while (totalGamesinRound != 0);

        /* std::cout << "Game 2: Human vs Robotn";
         game.play(robot1, robot1);*/
        std::cout << "Wins for " << robot1.m_type << ": Player : " << robot1.m_symbol << " - " << robot1.m_wins << "n";
        std::cout << "Wins for " << robot2.m_type << ": Player : " << robot2.m_symbol << " - " << robot2.m_wins << "n";
        std::cout << "Drawed: " << robot1.m_draws << "n";

        auto playerWithMostWins = std::max_element(player.begin(), player.end(),
            ()(const auto& lhs, const auto& rhs)
            {
                return lhs.m_wins < rhs.m_wins;
            });

        std::cout << "Winner of round " << roundCount << " is " << playerWithMostWins->m_symbol << "n";
        round--;
    } 
    while (round != 0);
}

interview – Why companies do not consider JavaScript as one of the OOP languages?

I often see job postings from companies who are looking to hire an engineer mentioning they are looking for someone with an experience with any of the object-oriented languages and they list a bunch of languages in brackets and often JavaScript is not one of them. Is there a specific reason for this? JavaScript is an object-oriented language by definition. It just simply not class-based. Misunderstanding or lack of industry knowledge, maybe? What do you think?