class_docstr: Parse input objects from the user into a metric query specification.
  
  Input objects are converted into *ResolverInput objects and then passed to the query resolver. The query resolver
  returns concrete specs corresponding to the request input / issues with query resolution.
  
  TODO: Add fuzzy match results.
class_docstr_tok: ["Parse","input","objects","from","the","user","into","a","metric","query","specification",".","Input","objects","are","converted","into","*","ResolverInput","objects","and","then","passed","to","the","query","resolver",".","The","query","resolver","returns","concrete","specs","corresponding","to","the","request","input","\/","issues","with","query","resolution",".","TODO",":","Add","fuzzy","match","results","."]
code: def _parse_and_validate_query(
      self,
      metric_names: Optional[Sequence[str]],
      metrics: Optional[Sequence[MetricQueryParameter]],
      group_by_names: Optional[Sequence[str]],
      group_by: Optional[Tuple[GroupByParameter, ...]],
      limit: Optional[int],
      time_constraint_start: Optional[datetime.datetime],
      time_constraint_end: Optional[datetime.datetime],
      where_constraint: Optional[WhereFilter],
      where_constraint_str: Optional[str],
      order_by_names: Optional[Sequence[str]],
      order_by: Optional[Sequence[OrderByQueryParameter]],
      min_max_only: bool,
  ) -> MetricFlowQuerySpec:
      # TODO: validate min_max_only - can only be called for non-metric queries
      assert_at_most_one_arg_set(
          metric_names=metric_names, metrics=metrics
      )
      assert_at_most_one_arg_set(
          group_by_names=group_by_names, group_by=group_by
      )
      assert_at_most_one_arg_set(
          order_by_names=order_by_names, order_by=order_by
      )
      assert_at_most_one_arg_set(
          where_constraint=where_constraint,
          where_constraint_str=where_constraint_str,
      )
  
      metric_names = metric_names or ()
      metrics = metrics or ()
  
      group_by_names = group_by_names or ()
      group_by = group_by or ()
  
      order_by_names = order_by_names or ()
      order_by = order_by or ()
  
      input_to_issue_set_mapping_item: List[
          InputToIssueSetMappingItem
      ] = []
  
      resolver_inputs_for_metrics: List[ResolverInputForMetric] = []
      for metric_name in metric_names:
          resolver_input_for_metric: Optional[
              MetricFlowQueryResolverInput
          ] = None
          for metric_naming_scheme in self._metric_naming_schemes:
              if metric_naming_scheme.input_str_follows_scheme(
                  metric_name
              ):
                  resolver_input_for_metric = ResolverInputForMetric(
                      input_obj=metric_name,
                      naming_scheme=metric_naming_scheme,
                      spec_pattern=metric_naming_scheme.spec_pattern(
                          metric_name
                      ),
                  )
                  resolver_inputs_for_metrics.append(
                      resolver_input_for_metric
                  )
                  break
          if resolver_input_for_metric is None:
              resolver_input_for_metric = InvalidStringInput(
                  metric_name
              )
              input_to_issue_set_mapping_item.append(
                  InputToIssueSetMappingItem(
                      resolver_input=resolver_input_for_metric,
                      issue_set=MetricFlowQueryResolutionIssueSet.from_issue(
                          StringInputParsingIssue.from_parameters(
                              input_str=metric_name,
                          )
                      ),
                  )
              )
  
      for metric_query_parameter in metrics:
          resolver_inputs_for_metrics.append(
              metric_query_parameter.query_resolver_input
          )
  
      resolver_inputs_for_group_by_items: List[
          ResolverInputForGroupByItem
      ] = []
      for group_by_name in group_by_names:
          resolver_input_for_group_by_item: Optional[
              MetricFlowQueryResolverInput
          ] = None
          for (
              group_by_item_naming_scheme
          ) in self._group_by_item_naming_schemes:
              if group_by_item_naming_scheme.input_str_follows_scheme(
                  group_by_name
              ):
                  spec_pattern = (
                      group_by_item_naming_scheme.spec_pattern(
                          group_by_name
                      )
                  )
                  resolver_input_for_group_by_item = ResolverInputForGroupByItem(
                      input_obj=group_by_name,
                      input_obj_naming_scheme=group_by_item_naming_scheme,
                      spec_pattern=spec_pattern,
                  )
                  resolver_inputs_for_group_by_items.append(
                      resolver_input_for_group_by_item
                  )
                  break
          if resolver_input_for_group_by_item is None:
              resolver_input_for_group_by_item = InvalidStringInput(
                  group_by_name
              )
              input_to_issue_set_mapping_item.append(
                  InputToIssueSetMappingItem(
                      resolver_input=resolver_input_for_group_by_item,
                      issue_set=MetricFlowQueryResolutionIssueSet.from_issue(
                          StringInputParsingIssue.from_parameters(
                              input_str=group_by_name,
                          )
                      ),
                  )
              )
  
          logger.info(
              "Converted group-by-item input:\n"
              + indent(f"Input: {repr(group_by_name)}")
              + "\n"
              + indent(
                  f"Resolver Input: {mf_pformat(resolver_input_for_group_by_item)}"
              )
          )
  
      for group_by_parameter in group_by:
          resolver_input_for_group_by_parameter = (
              group_by_parameter.query_resolver_input
          )
          resolver_inputs_for_group_by_items.append(
              resolver_input_for_group_by_parameter
          )
          logger.info(
              "Converted group-by-item input:\n"
              + indent(f"Input: {repr(group_by_parameter)}")
              + "\n"
              + indent(
                  f"Resolver Input: {mf_pformat(resolver_input_for_group_by_parameter)}"
              )
          )
  
      where_filters: List[PydanticWhereFilter] = []
  
      if where_constraint is not None:
          where_filters.append(
              PydanticWhereFilter(
                  where_sql_template=where_constraint.where_sql_template
              )
          )
      if where_constraint_str is not None:
          where_filters.append(
              PydanticWhereFilter(
                  where_sql_template=where_constraint_str
              )
          )
  
      resolver_input_for_filter = (
          ResolverInputForQueryLevelWhereFilterIntersection(
              where_filter_intersection=PydanticWhereFilterIntersection(
                  where_filters=where_filters
              )
          )
      )
  
      query_resolver = MetricFlowQueryResolver(
          manifest_lookup=self._manifest_lookup,
          where_filter_pattern_factory=self._where_filter_pattern_factory,
      )
  
      resolver_inputs_for_order_by: List[
          ResolverInputForOrderByItem
      ] = []
      resolver_inputs_for_order_by.extend(
          self._parse_order_by_names(
              order_by_names=order_by_names,
          )
      )
      resolver_inputs_for_order_by.extend(
          MetricFlowQueryParser._parse_order_by(order_by=order_by)
      )
  
      resolver_input_for_limit = ResolverInputForLimit(limit=limit)
      resolver_input_for_min_max_only = ResolverInputForMinMaxOnly(
          min_max_only=min_max_only
      )
  
      resolver_input_for_query = ResolverInputForQuery(
          metric_inputs=tuple(resolver_inputs_for_metrics),
          group_by_item_inputs=tuple(
              resolver_inputs_for_group_by_items
          ),
          order_by_item_inputs=tuple(resolver_inputs_for_order_by),
          limit_input=resolver_input_for_limit,
          filter_input=resolver_input_for_filter,
          min_max_only=resolver_input_for_min_max_only,
      )
  
      logger.info(
          "Resolver input for query is:\n"
          + indent(mf_pformat(resolver_input_for_query))
      )
  
      query_resolution = query_resolver.resolve_query(
          resolver_input_for_query
      )
  
      logger.info(
          "Query resolution is:\n"
          + indent(mf_pformat(query_resolution))
      )
  
      self._raise_exception_if_there_are_errors(
          input_to_issue_set=query_resolution.input_to_issue_set.merge(
              InputToIssueSetMapping(
                  tuple(input_to_issue_set_mapping_item)
              )
          ),
      )
  
      query_spec = query_resolution.checked_query_spec
      assert query_resolution.resolution_dag is not None
      # Can't compute the grain of metric_time if there are no metrics.
      if (
          time_constraint_start is not None
          or time_constraint_end is not None
      ) and len(resolver_inputs_for_metrics) > 0:
          if time_constraint_start is None:
              time_constraint_start = (
                  TimeRangeConstraint.ALL_TIME_BEGIN()
              )
              logger.info(
                  f"time_constraint_start was None, so it was set to {time_constraint_start}"
              )
          if time_constraint_end is None:
              time_constraint_end = TimeRangeConstraint.ALL_TIME_END()
              logger.info(
                  f"time_constraint_end was None, so it was set to {time_constraint_end}"
              )
  
          time_constraint = TimeRangeConstraint(
              start_time=time_constraint_start,
              end_time=time_constraint_end,
          )
  
          time_constraint = self._adjust_time_constraint(
              resolution_dag=query_resolution.resolution_dag,
              time_dimension_specs_in_query=query_spec.time_dimension_specs,
              time_constraint=time_constraint,
          )
          logger.info(
              f"Time constraint after adjustment is: {time_constraint}"
          )
  
          return query_spec.with_time_range_constraint(time_constraint)
  
      return query_spec
  
imports: from __future__ import annotations
  import datetime
  import logging
  from typing import List, Optional, Sequence, Tuple, Union
  import pandas
  from dbt_semantic_interfaces.implementations.filters.where_filter import PydanticWhereFilter, PydanticWhereFilterIntersection
  from dbt_semantic_interfaces.pretty_print import pformat_big_objects
  from dbt_semantic_interfaces.protocols import SavedQuery
  from dbt_semantic_interfaces.protocols.where_filter import WhereFilter
  from dbt_semantic_interfaces.type_enums import TimeGranularity
  from metricflow.assert_one_arg import assert_at_most_one_arg_set
  from metricflow.filters.merge_where import merge_to_single_where_filter
  from metricflow.filters.time_constraint import TimeRangeConstraint
  from metricflow.mf_logging.formatting import indent
  from metricflow.mf_logging.pretty_print import mf_pformat
  from metricflow.mf_logging.runtime import log_runtime
  from metricflow.model.semantic_manifest_lookup import SemanticManifestLookup
  from metricflow.naming.dunder_scheme import DunderNamingScheme
  from metricflow.naming.metric_scheme import MetricNamingScheme
  from metricflow.naming.object_builder_scheme import ObjectBuilderNamingScheme
  from metricflow.protocols.query_parameter import GroupByParameter, MetricQueryParameter, OrderByQueryParameter, SavedQueryParameter
  from metricflow.query.group_by_item.filter_spec_resolution.filter_pattern_factory import DefaultWhereFilterPatternFactory, WhereFilterPatternFactory
  from metricflow.query.group_by_item.group_by_item_resolver import GroupByItemResolver
  from metricflow.query.group_by_item.resolution_dag.dag import GroupByItemResolutionDag
  from metricflow.query.issues.issues_base import MetricFlowQueryResolutionIssueSet
  from metricflow.query.issues.parsing.string_input_parsing_issue import StringInputParsingIssue
  from metricflow.query.query_exceptions import InvalidQueryException
  from metricflow.query.query_resolution import InputToIssueSetMapping, InputToIssueSetMappingItem
  from metricflow.query.query_resolver import MetricFlowQueryResolver
  from metricflow.query.resolver_inputs.base_resolver_inputs import MetricFlowQueryResolverInput
  from metricflow.query.resolver_inputs.query_resolver_inputs import InvalidStringInput, ResolverInputForGroupByItem, ResolverInputForLimit, ResolverInputForMetric, ResolverInputForMinMaxOnly, ResolverInputForOrderByItem, ResolverInputForQuery, ResolverInputForQueryLevelWhereFilterIntersection
  from metricflow.specs.patterns.base_time_grain import BaseTimeGrainPattern
  from metricflow.specs.patterns.metric_time_pattern import MetricTimePattern
  from metricflow.specs.patterns.none_date_part import NoneDatePartPattern
  from metricflow.specs.specs import InstanceSpec, InstanceSpecSet, MetricFlowQuerySpec, TimeDimensionSpec
  from metricflow.time.time_granularity import adjust_to_end_of_period, adjust_to_start_of_period, is_period_end, is_period_start
label_desc: Use image node_id 11 for calling the MetricFlowQueryParser obj's underlying member method code with example usage: obj._parse_and_validate_query(metric_names, metrics, group_by_names, group_by, limit, time_constraint_start, time_constraint_end, where_constraint, where_constraint_str, order_by_names, order_by, min_max_only) and returns: query_spec, query_spec