在Python中过滤对象的用户界面

2024-10-02 10:24:09 发布

您现在位置:Python中文网/ 问答频道 /正文

在我的应用程序中,我有一个定义/概述如下的工作类。此作业类的实例表示特定作业运行。作业可以有多个检查点,每个检查点可以有多个命令。在

Job
 - JobName
 - [JobCheckpoint]
 - StartTime
 - EndTime
 - Status
 - ...

JobCheckpoint
 - JobCheckpointName
 - [JobCommand]
 - StartTime
 - EndTime
 - Status
 - ...

JobCommand
 - JobCommandName
 - [Command]
 - StartTime
 - EndTime
 - Status 
 - ...

在任何一天都有大约10万个不同的工作在运行。作业信息保存在文件系统中。我想用Python设计一个用于查询这些作业对象的用户界面。例如,用户应该能够查询

  1. 在x和y间隔之间运行的所有作业。在
  2. 运行命令x的所有作业
  3. 所有作业都处于失败状态。在
  4. 所有作业都处于失败和终止状态。在
  5. 特定作业的所有检查点/命令。在
  6. 还有更多。。。在

为了解决这个问题,我考虑在用户界面中提供以下方法。在

^{pr2}$

我不知道如何用Python设计这个过滤器类

  1. 支持对作业对象的所有此类查询。在
  2. 并使用户对API的使用简单直观。在

这里的线索真的很感谢。在


Tags: 对象实例用户命令应用程序定义状态status
1条回答
网友
1楼 · 发布于 2024-10-02 10:24:09

这些都是部分主观问题。但我会尽我目前所知和所提问题中的可用信息来回答其中一些问题。在

过滤器类将是什么样子?

这可能取决于存储机制。它是作为一堆Python对象存储在内存中,还是首先从SQL数据库或NoSQL数据库中取出。在

如果它是从SQL数据库获取的,那么可以利用SQL的过滤机制。它毕竟是一种(结构化)查询语言。在

在这种情况下,过滤器类就像一个将字段值转换成一堆SQL运算符/条件的转换器。在

如果是一堆Python对象,而没有用于查询数据的数据库机制,那么您可能需要考虑自己的查询/筛选方法。在

筛选器类可能正在使用条件类和运算符类。也许你有一个操作符类作为一个抽象类,并且有“glue”操作符将条件粘合在一起(and/OR)。以及另一种运算符,用于将域对象的属性与值进行比较。在

对于后者,即使您没有为其设计“过滤语言”,也可以从API查询格式中获得一些灵感,该格式在这里为Flask untillet:https://flask-restless.readthedocs.io/en/stable/searchformat.html#query-format

当然,如果您正在设计一个查询接口,例如restapi,Flask neurgent的查询格式可以给您一些关于如何处理查询的启发。在

返回的域对象列表是正确的还是应该返回dict的列表?

返回域对象列表的优点是可以使用继承。这至少是一个可能的优势。在

某些类别的草图:

from abc import ABCMeta, abstractmethod
from typing import List

class DomainObjectOperatorGlue(metaclass=ABCMeta):    
    @abstractmethod
    def operate(self, haystack: List['DomainObject'], criteria: 
        List['DomainObject']) -> List['DomainObject']:
        pass

class DomainObjectFieldGlueOperator(metaclass=ABCMeta):
    @abstractmethod
    def operate(self, conditions: List[bool]) -> bool:
        pass

class DomainObjectFieldGlueOperatorAnd(DomainObjectFieldGlueOperator):
    def operate(self, conditions: List[bool]) -> bool:
        # If all conditions are True then return True here,
        # otherwise return False.
        # (...)
        pass

class DomainObjectFieldGlueOperatorOr(DomainObjectFieldGlueOperator):
    def operate(self, conditions: List[bool]) -> bool:
        # If only one (or more) of the conditions are True then return True
        # otherwise, if none are True, return False.
        # (...)
        pass


class DomainObjectOperatorAnd(DomainObjectOperatorGlue):
    def __init__(self):
        pass

    def operate(self, haystack: 'JobsCollection', criteria: 
List['DomainObject']) -> List['DomainObject']:
        """
        Returns list of haystackelements or empty list.
        Includes haystackelement if all (search) 'criteria' elements 
(DomainObjects) are met for haystackelement (DomainObject).
        """
        result = []
        for haystackelement in haystack.jobs:
            # AND operator wants all criteria to be True for haystackelement (Job)
        # to be included in returned search results.
        criteria_all_true_for_haystackelement = True
        for criterium in criteria:
            if haystackelement.excludes(criterium):
                criteria_all_true_for_haystackelement = False
                break
        if criteria_all_true_for_haystackelement:
            result.append(haystackelement)
    return result

class DomainObjectOperatorOr(DomainObjectOperatorGlue):
    def __init__(self):
        pass

def operate(self, haystack: List['DomainObject'], criteria: List['DomainObject']) -> List['DomainObject']:
    """
    Returns list of haystackelements or empty list.
    Includes haystackelement if all (search) 'criteria' elements (DomainObjects) are met for haystackelement (DomainObject).
    """
    result = []
    for haystackelement in haystack:
        # OR operator wants at least ONE criterium to be True for haystackelement
        # to be included in returned search results.
        at_least_one_criterium_true_for_haystackelement = False
        for criterium in criteria:
            if haystackelement.matches(criterium):
                at_least_one_criterium_true_for_haystackelement = True
                break
        if at_least_one_criterium_true_for_haystackelement:
            result.append(haystackelement)
    return result

class DomainObjectFilter(metaclass=ABCMeta):
    def __init__(self, criteria: List['DomainObject'], criteria_glue: 
DomainObjectOperatorGlue):
        self.criteria = criteria
        self.criteria_glue = criteria_glue

    @abstractmethod
    def apply(self, haystack: 'JobsCollection') -> List['DomainObject']:
        """
       Applies filter to given 'haystack' (list of jobs with sub-objects in there);
    returns filtered list of DomainObjects or empty list if none found
    according to criteria (and criteria glue).
        """
        return self.criteria_glue.operate(haystack, self.criteria)

class DomainObject(metaclass=ABCMeta):
    def __init__(self):
        pass

    @abstractmethod
    def matches(self, domain_object: 'DomainObject') -> bool:
        """ Returns True if this DomainObject matches specified DomainObject,
    False otherwise.
     """
    pass

def excludes(self, domain_object: 'DomainObject') -> bool:
    """
    Convenience method; the inverse of includes-method.
    """
    return not self.matches(domain_object)


class Job(DomainObject):
    def __init__(self, name, start, end, status, job_checkpoints: 
List['JobCheckpoint']):
        self.name = name
        self.start = start
        self.end = end
        self.status = status
        self.job_checkpoints = job_checkpoints

    def matches(self, domain_object: 'DomainObject', field_glue: 
DomainObjectFieldGlueOperator) -> bool:
        """
        Returns True if this DomainObject includes specified DomainObject,
     False otherwise.
         """
        if domain_object is Job:
            # See if specified fields in search criteria (domain_object/Job) matches this job.
            # Determine here which fields user did not leave empty,
            # and guess for sensible search criteria.
            # Return True if it's  a match, False otherwise.
            condition_results = []
            if domain_object.name != None:
                condition_results.append(domain_object.name in self.name)
            if domain_object.start != None or domain_object.end != None:
                if domain_object.start == None:
                    # ...Use broadest start time for criteria here...
                    # time_range_condition = ...
                    condition_results.append(time_range_condition)                 
                elif domain_object.end == None:
                    # ...Use broadest end time for criteria here...
                    # time_range_condition = ...
                    condition_results.append(time_range_condition)                 
                else:
                    # Both start and end time specified; use specified time range.
                # time_range_condition = ...
                condition_results.append(time_range_condition)
            # Then evaluate condition_results;
            # e.g. return True if all condition_results are True here,
            # false otherwise depending on implementation of field_glue class:
            return field_glue.operate(condition_results)
    elif domain_object is JobCheckpoint:
        # Determine here which fields user did not leave empty,
        # and guess for sensible search criteria.
        # Return True if it's  a match, False otherwise.
        # First establish if parent of JobCheckpoint is 'self' (this job)
        # if so, then check if search criteria for JobCheckpoint match,
        # glue fields with something like:
        return field_glue.operate(condition_results)
    elif domain_object is JobCommand:
        # (...)
        if domain_object.parent_job == self:
            # see if conditions pan out
            return field_glue.operate(condition_results)

class JobCheckpoint(DomainObject):
    def __init__(self, name, start, end, status, job_commands: List['JobCommand'], parent_job: Job):
       self.name = name
        self.start = start
        self.end = end
        self.status = status
       self.job_commands = job_commands
        # For easier reference;
        # e.g. when search criteria matches this JobCheckpoint
        # then Job associated to it can be found
        # more easily.
        self.parent_job = parent_job

class JobCommand(DomainObject):
    def __init__(self, name, start, end, status, parent_checkpoint: JobCheckpoint, parent_job: Job):
        self.name = name
        self.start = start
        self.end = end
        self.status = status
        # For easier reference;
        # e.g. when search criteria matches this JobCommand
        # then Job or JobCheckpoint associated to it can be found
        # more easily.
        self.parent_checkpoint = parent_checkpoint
        self.parent_job = parent_job

class JobsCollection(DomainObject):
    def __init__(self, jobs: List['Job']):
         self.jobs = jobs

    def get_jobs(self, filter: DomainObjectFilter) -> List[Job]:
        return filter.apply(self)

    def get_commands(self, job: Job) -> List[JobCommand]:
        """
        Returns all commands for specified job (search criteria).
        """
        result = []
        for some_job in self.jobs:
            if job.matches(some_job):
                for job_checkpoint in job.job_checkpoints:
                    result.extend(job_checkpoint.job_commands)
         return result

    def get_checkpoints(self, job: Job) -> List[JobCheckpoint]:
        """
        Returns all checkpoints for specified job (search criteria).
        """
        result = []
        for some_job in self.jobs:
            if job.matches(some_job):
                result.extend(job.job_checkpoints)
        return result

相关问题 更多 >

    热门问题