def generate(self, goal_slot_selection_weights=None):
        """
        Generate a new complex goal

        :param goal_slot_selection_weights: list of weights that bias the
                                            sampling of slots for constraints
        :return: a new goal
        """
        if self.goals:
            return random.sample(self.goals)

        # Randomly pick an item from the database
        cursor = self.database.SQL_connection.cursor()

        sql_command = "SELECT * FROM " + self.db_table_name + \
                      " WHERE ROWID == (" + \
                      str(random.randint(1, self.db_row_count)) + ");"

        cursor.execute(sql_command)
        db_result = cursor.fetchone()

        global_key_value = ''
        global_attempt = 0

        result = []

        while global_attempt < 3 and not global_key_value:
            attempt = 0
            while attempt < 3 and not db_result:
                print('GoalGenerator: Database {0} appears to be empty!'
                      .format(self.database))
                print(f'Trying again (attempt {attempt} out of 3)...')

                sql_command = "SELECT * FROM " + self.db_table_name + \
                              " WHERE ROWID == (" + \
                              str(random.randint(1, self.db_row_count)) + ");"

                cursor.execute(sql_command)
                db_result = cursor.fetchone()

                attempt += 1

            if not db_result:
                raise LookupError('GoalGenerator: Database {0} appears to be '
                                  'empty!'.format(self.database))

            result = dict(zip(self.slot_names, db_result))

            if self.global_key in result:
                global_key_value = result[self.global_key]

            global_attempt += 1

        if not result:
            raise LookupError(f'ComplexGoalGenerator cannot find an item with '
                              f'global key {self.global_key}')

        # Generate goal
        goal = Goal()

        # Sample "global" constraints and requests that all sub-goals share
        global_inf_slots = \
            random.sample(self.global_slots,
                          random.randint(2, len(self.global_slots)))

        # Fetch all items from the database that satisfy the constraints
        # sampled above
        sql_command = "SELECT * FROM " + self.db_table_name + \
                      " WHERE " + self.global_key + " = \"" + \
                      global_key_value + "\" AND "

        for gs in global_inf_slots:
            sql_command += gs + " = \"" + result[gs] + "\" AND "
        # Trim last 'AND '
        sql_command = sql_command[:-4] + ";"

        cursor.execute(sql_command)
        db_results = cursor.fetchall()

        results = [dict(zip(self.slot_names, dbr)) for dbr in db_results]

        for slot in global_inf_slots:
            # Check that the slot has a value in the retrieved item
            if slot in result and result[slot]:
                if result[slot] not in ['None', 'Other']:
                    goal.constraints[slot] = \
                        DialogueActItem(slot, Operator.EQ, result[slot])

        # Sample requests
        global_req_slots = \
            random.sample(self.global_slots,
                          random.randint(0, len(self.global_slots)))
        print('DBG: Global Req Sampled')

        # Remove slots for which the user places constraints
        # Note: 'name' may or may not be in inf_slots here, and this is
        # randomness is desirable
        for slot in global_inf_slots:
            if slot in global_req_slots:
                global_req_slots.remove(slot)

        for slot in global_req_slots:
            if slot in result:
                goal.requests[slot] = DialogueActItem(slot, Operator.EQ, [])

        # Sample number of sub-goals
        num_sub_goals = \
            random.choices(range(1, 5), weights=[0.15, 0.35, 0.35, 0.15])[0]

        # Make sure we don't attempt to sample more subgoals than items in the
        # results
        num_sub_goals = \
            num_sub_goals if len(results) > num_sub_goals else len(results)

        subgoal_attempts = 0

        # As there is no guarantee that the sampled slots for the sampled
        # subgoals exist (and we do not want empty subgoals), make three
        # attempts at sampling subgoals.
        while not goal.subgoals and subgoal_attempts < 3:
            print(f'DBG: Sampling Results {len(results)}, {num_sub_goals}')
            results = random.sample(results, num_sub_goals)
            subgoal_attempts += 1

            # For each sub-goal, sample "local" constraints and requests
            # (must be on different slots than the global)
            for sg in range(num_sub_goals):
                local_inf_slots = \
                    random.sample(self.local_slots,
                                  random.randint(1, len(self.local_slots)))

                # Create new subgoal
                subgoal = Goal()

                for lif in local_inf_slots:
                    # Check that the slot has indeed a value
                    if results[sg][lif] and \
                            results[sg][lif] not in ['None', 'Other']:
                        subgoal.constraints[lif] = \
                            DialogueActItem(lif, Operator.EQ, results[sg][lif])

                if subgoal.constraints:
                    goal.subgoals.append(subgoal)

        # TODO Check if a constraint exists in all subgoals, in which case
        # remove from all subgoals and put in global constr

        return goal

        Generate a new complex goal

        :param goal_slot_selection_weights: list of weights that bias the
                                            sampling of slots for constraints
        :return: a new goal
        
plato/agent/component/user_simulator/goal.py