This method generates the SAS code from the python objects and included data and arguments.
        The list of args in this method is largely alphabetical but there are exceptions in order to
        satisfy the order needs of the statements for the procedure. as an example...
        http://support.sas.com/documentation/cdl/en/statug/68162/HTML/default/viewer.htm#statug_glm_syntax.htm#statug.glm.glmpostable

        :param objtype: str -- proc name
        :param objname: str -- 3 digit code for proc
        :param data: sas dataset object
        :param args: dict --  proc arguments
        :return: str -- the SAS code needed to execute on the server

def _makeProcCallMacro(self, objtype: str, objname: str, data: ['SASdata', str] = None, args: dict = None) -> str:
        """
        This method generates the SAS code from the python objects and included data and arguments.
        The list of args in this method is largely alphabetical but there are exceptions in order to
        satisfy the order needs of the statements for the procedure. as an example...
        http://support.sas.com/documentation/cdl/en/statug/68162/HTML/default/viewer.htm#statug_glm_syntax.htm#statug.glm.glmpostable

        :param objtype: str -- proc name
        :param objname: str -- 3 digit code for proc
        :param data: sas dataset object
        :param args: dict --  proc arguments
        :return: str -- the SAS code needed to execute on the server
        """
        plot = ''
        outmeth = ''
        procopts = args.pop('procopts', '')
        # Set ODS graphic generation to True by default
        ODSGraphics = args.get('ODSGraphics', True)

        # The different SAS products vary slightly in plotting and out methods.
        # this block sets the options correctly for plotting and output statements
        if self.sasproduct.lower() == 'stat' and not ('ODSGraphics' in args.keys() or ODSGraphics == False):
            plot = 'plot=all'
        if self.sasproduct.lower() == 'qc':
            pass
        if self.sasproduct.lower() == 'ets' and not ('ODSGraphics' in args.keys() or ODSGraphics == False):
            outmeth = 'out'
            plot = 'plot=all'
        if self.sasproduct.lower() == 'em':

            pass
        if self.sasproduct.lower() == 'vddml':
            outmeth = 'out'
        if self.sasproduct.lower() == 'util':
            outmeth = 'out'
            if objtype.lower() =='univariate' and not ('ODSGraphics' in args.keys() or ODSGraphics == False):
                plot = 'plot'
                outmeth = ''
        outds = args.pop('out', None)
        if outds == None:
            outds = args.pop('output', None)
        outcodegen = Codegen.new('out', outds)
        outcodegen.outmeth = outmeth
        outcodegen.objname = objname
        outstr = outcodegen.codestmt
        self.logger.debug("product caller: " + self.sasproduct.lower())
        debug_code= ''
        code = "%macro proccall(d);\n"
        # resolve issues withe Proc options, out= and plots=
        # The procopts statement should be in every procedure as a way to pass arbitrary options to the procedures
        if 'outmeth' in args:
            outmeth = args['outmeth']
        if 'plot' in args:
            plot = args['plot']
        if len(outmeth) and not outds == None:
            #outstr = outds.libref + '.' + outds.table
            code += "proc %s data=%s.%s%s %s %s=%s %s ;\n" % (
                objtype, data.libref, data.table, data._dsopts(), plot, outmeth, outstr, procopts)
        else:
            code += "proc %s data=%s.%s%s %s %s ;\n" % (
            objtype, data.libref, data.table, data._dsopts(), plot, procopts)
            if outds is not None:
                args['output'] = outds
        self.logger.debug("args value: " + str(args))
        self.logger.debug("args type: " + str(type(args)))

        # this list is largely alphabetical but there are exceptions in order to
        # satisfy the order needs of the statements for the procedures
        # as an example...
        # http://support.sas.com/documentation/cdl/en/statug/68162/HTML/default/viewer.htm#statug_glm_syntax.htm#statug.glm.glmpostable

        uoargs = {}
        orderedargs = {}
        keyorder = ['by', 'input', 'target', 'cls', 'model', 'output']
        for k, v in args.items():
            if k in keyorder:
                orderedargs[k] = v
            else:
                uoargs[k] = v
        orderedargs = OrderedDict(sorted(orderedargs.items(), key=lambda i: keyorder.index(i[0])))
        for k, v in uoargs.items():
            orderedargs[k] = v
            orderedargs.move_to_end(k)
        for key, value in orderedargs.items():
            gen = Codegen.new(key, value)
            gen.objtype = objtype
            gen.data = data
            gen.outmeth = outmeth
            gen.objname = objname
            code += gen.codestmt
            if gen.debug is not None:
                debug_code += gen.debug

        code += "run; quit; %mend;\n"
        code += "%%mangobj(%s,%s,%s);" % (objname, objtype, data.table)
        if self.logger.level == 10:
            print("Proc code submission:\n " + str(code))
            print("\n\n\n" + debug_code)
        return code