def current(cls, entity_id): """ Return the active data entry, if any, otherwise None """ cached = cache.get(cls.cache_key_name(entity_id)) if cached is not None: return cached try: current = cls.objects.filter(entity_id=entity_id).order_by('-fetched_at')[0] except IndexError: current = None cache.set(cls.cache_key_name(entity_id), current, cls.cache_timeout) return current
def current(cls, site=None, org=None, course_key=None): # pylint: disable=arguments-differ """ Return the current overridden configuration at the specified level. Only one level may be specified at a time. Specifying multiple levels will result in a ValueError. For example, considering the following set of requirements: Global: Feature Disabled edx.org (Site): Feature Enabled Harvard (org): Feature Disabled CS50 (course): Feature Enabled Assuming these values had been properly configured, these calls would result MyStackedConfig.current() # False MyStackedConfig.current(site=Site(domain='edx.org')) # True MyStackedConfig.current(site=Site(domain='whitelabel.edx.org')) # False -- value derived from global setting MyStackedConfig.current(org='HarvardX') # False MyStackedConfig.current(org='MITx') # True -- value derived from edx.org site MyStackedConfig.current(course=CourseKey(org='HarvardX', course='CS50', run='2018_Q1')) # True cs50 = CourseKey(org='HarvardX', course='Bio101', run='2018_Q1') MyStackedConfig.current(course=cs50) # False -- value derived from HarvardX org The following calls would result in errors due to overspecification: MyStackedConfig.current(site=Site(domain='edx.org'), org='HarvardX') MyStackedConfig.current(site=Site(domain='edx.org'), course=cs50) MyStackedConfig.current(org='HarvardX', course=cs50) Arguments: site: The Site to check current values for org: The org to check current values for course: The course to check current values for Returns: An instance of :class:`cls.attribute_tuple()` with the overridden values specified down to the level of the supplied argument (or global values if no arguments are supplied). """ cache_key_name = cls.cache_key_name(site, org, course_key=course_key) cached = cache.get(cache_key_name) if cached is not None: return cached # Raise an error if more than one of site/org/course are specified simultaneously. if len([arg for arg in [site, org, course_key] if arg is not None]) > 1: raise ValueError("Only one of site, org, and course can be specified") if org is None and course_key is not None: org = cls._org_from_course_key(course_key) if site is None and org is not None: site = cls._site_from_org(org) stackable_fields = [cls._meta.get_field(field_name) for field_name in cls.STACKABLE_FIELDS] field_defaults = { field.name: field.get_default() for field in stackable_fields } values = field_defaults.copy() global_override_q = Q(site=None, org=None, course_id=None) site_override_q = Q(site=site, org=None, course_id=None) org_override_q = Q(site=None, org=org, course_id=None) course_override_q = Q(site=None, org=None, course_id=course_key) overrides = cls.objects.current_set().filter( global_override_q | site_override_q | org_override_q | course_override_q ).order_by( # Sort nulls first, and in reverse specificity order # so that the overrides are in the order of general to specific. # # Site | Org | Course # -------------------- # Null | Null | Null # site | Null | Null # Null | org | Null # Null | Null | Course F('course').desc(nulls_first=True), F('org').desc(nulls_first=True), F('site').desc(nulls_first=True), ) for override in overrides: for field in stackable_fields: value = field.value_from_object(override) if value != field_defaults[field.name]: values[field.name] = value current = cls(**values) cache.set(cache_key_name, current, cls.cache_timeout) return current
def current(cls, site=None, org=None, org_course=None, course_key=None): # pylint: disable=arguments-differ """ Return the current overridden configuration at the specified level. Only one level may be specified at a time. Specifying multiple levels will result in a ValueError. For example, considering the following set of requirements: Global: Feature Disabled edx.org (Site): Feature Enabled HarvardX (org): Feature Disabled HarvardX/CS50 (org_course): Feature Enabled CS50 in 2018_Q1 (course run): Feature Disabled Assuming these values had been properly configured, these calls would result MyStackedConfig.current() # False MyStackedConfig.current(site=Site(domain='edx.org')) # True MyStackedConfig.current(site=Site(domain='whitelabel.edx.org')) # False -- value derived from global setting MyStackedConfig.current(org='HarvardX') # False MyStackedConfig.current(org='MITx') # True -- value derived from edx.org site MyStackedConfig.current(org_course='HarvardX/CS50') # True MyStackedConfig.current(org_course='HarvardX/Bio101') # False -- value derived from HarvardX setting MyStackedConfig.current(course_key=CourseKey(org='HarvardX', course='CS50', run='2018_Q1')) # False MyStackedConfig.current( course_key=CourseKey(org='HarvardX', course='CS50', run='2019_Q1') ) # True -- value derived from HarvardX/CS50 setting bio101 = CourseKey(org='HarvardX', course='Bio101', run='2018_Q1') MyStackedConfig.current(course_key=cs50) # False -- value derived from HarvardX org The following calls would result in errors due to overspecification: MyStackedConfig.current(site=Site(domain='edx.org'), org='HarvardX') MyStackedConfig.current(site=Site(domain='edx.org'), course=cs50) MyStackedConfig.current(org='HarvardX', course=cs50) Arguments: site: The Site to check current values for org: The org to check current values for org_course: The course in a specific org to check current values for course_key: The course to check current values for Returns: An instance of :class:`cls.attribute_tuple()` with the overridden values specified down to the level of the supplied argument (or global values if no arguments are supplied). """ cache_key_name = cls.cache_key_name(site, org, org_course, course_key) cached = cache.get(cache_key_name) if cached is not None: return cached # Raise an error if more than one of site/org/course are specified simultaneously. if len([ arg for arg in [site, org, org_course, course_key] if arg is not None ]) > 1: raise ValueError( "Only one of site, org, org_course, and course can be specified" ) if org_course is None and course_key is not None: org_course = cls._org_course_from_course_key(course_key) if org is None and org_course is not None: org = cls._org_from_org_course(org_course) if site is None and org is not None: site = cls._site_from_org(org) stackable_fields = [ cls._meta.get_field(field_name) for field_name in cls.STACKABLE_FIELDS ] field_defaults = { field.name: field.get_default() for field in stackable_fields } values = field_defaults.copy() global_override_q = Q(site=None, org=None, org_course=None, course_id=None) site_override_q = Q(site=site, org=None, org_course=None, course_id=None) org_override_q = Q(site=None, org=org, org_course=None, course_id=None) org_course_override_q = Q(site=None, org=None, org_course=org_course, course_id=None) course_override_q = Q(site=None, org=None, org_course=None, course_id=course_key) overrides = cls.objects.current_set().filter(global_override_q | site_override_q | org_override_q | org_course_override_q | course_override_q) provenances = defaultdict(lambda: Provenance.default) # We are sorting in python to avoid doing a filesort in the database for # what will only be 4 rows at maximum def sort_key(override): """ Sort overrides in increasing specificity. This particular sort order sorts None before not-None (because False < True) It sorts global first (because all entries are None), then site entries (because course_id and org are None), then org, org_course and course (by the same logic) """ return (override.course_id is not None, override.org_course is not None, override.org is not None, override.site_id is not None) for override in sorted(overrides, key=sort_key): for field in stackable_fields: value = field.value_from_object(override) if value != field_defaults[field.name]: values[field.name] = value if override.course_id is not None: provenances[field.name] = Provenance.run elif override.org_course is not None: provenances[field.name] = Provenance.org_course elif override.org is not None: provenances[field.name] = Provenance.org elif override.site_id is not None: provenances[field.name] = Provenance.site else: provenances[field.name] = Provenance.global_ current = cls(**values) current.provenances = { field.name: provenances[field.name] for field in stackable_fields } # pylint: disable=attribute-defined-outside-init cache.set(cache_key_name, current, cls.cache_timeout) return current
def current(cls, site=None, org=None, course_key=None): # pylint: disable=arguments-differ """ Return the current overridden configuration at the specified level. Only one level may be specified at a time. Specifying multiple levels will result in a ValueError. For example, considering the following set of requirements: Global: Feature Disabled edx.org (Site): Feature Enabled Harvard (org): Feature Disabled CS50 (course): Feature Enabled Assuming these values had been properly configured, these calls would result MyStackedConfig.current() # False MyStackedConfig.current(site=Site(domain='edx.org')) # True MyStackedConfig.current(site=Site(domain='whitelabel.edx.org')) # False -- value derived from global setting MyStackedConfig.current(org='HarvardX') # False MyStackedConfig.current(org='MITx') # True -- value derived from edx.org site MyStackedConfig.current(course=CourseKey(org='HarvardX', course='CS50', run='2018_Q1')) # True cs50 = CourseKey(org='HarvardX', course='Bio101', run='2018_Q1') MyStackedConfig.current(course=cs50) # False -- value derived from HarvardX org The following calls would result in errors due to overspecification: MyStackedConfig.current(site=Site(domain='edx.org'), org='HarvardX') MyStackedConfig.current(site=Site(domain='edx.org'), course=cs50) MyStackedConfig.current(org='HarvardX', course=cs50) Arguments: site: The Site to check current values for org: The org to check current values for course: The course to check current values for Returns: An instance of :class:`cls.attribute_tuple()` with the overridden values specified down to the level of the supplied argument (or global values if no arguments are supplied). """ cache_key_name = cls.cache_key_name(site, org, course_key=course_key) cached = cache.get(cache_key_name) if cached is not None: return cached # Raise an error if more than one of site/org/course are specified simultaneously. if len([arg for arg in [site, org, course_key] if arg is not None]) > 1: raise ValueError( "Only one of site, org, and course can be specified") if org is None and course_key is not None: org = cls._org_from_course_key(course_key) if site is None and org is not None: site = cls._site_from_org(org) stackable_fields = [ cls._meta.get_field(field_name) for field_name in cls.STACKABLE_FIELDS ] field_defaults = { field.name: field.get_default() for field in stackable_fields } values = field_defaults.copy() global_override_q = Q(site=None, org=None, course_id=None) site_override_q = Q(site=site, org=None, course_id=None) org_override_q = Q(site=None, org=org, course_id=None) course_override_q = Q(site=None, org=None, course_id=course_key) overrides = cls.objects.current_set().filter( global_override_q | site_override_q | org_override_q | course_override_q ).order_by( # Sort nulls first, and in reverse specificity order # so that the overrides are in the order of general to specific. # # Site | Org | Course # -------------------- # Null | Null | Null # site | Null | Null # Null | org | Null # Null | Null | Course F('course').desc(nulls_first=True), F('org').desc(nulls_first=True), F('site').desc(nulls_first=True), ) for override in overrides: for field in stackable_fields: value = field.value_from_object(override) if value != field_defaults[field.name]: values[field.name] = value current = cls(**values) cache.set(cache_key_name, current, cls.cache_timeout) return current
def current(cls, site=None, org=None, org_course=None, course_key=None): # pylint: disable=arguments-differ """ Return the current overridden configuration at the specified level. Only one level may be specified at a time. Specifying multiple levels will result in a ValueError. For example, considering the following set of requirements: Global: Feature Disabled edx.org (Site): Feature Enabled HarvardX (org): Feature Disabled HarvardX/CS50 (org_course): Feature Enabled CS50 in 2018_Q1 (course run): Feature Disabled Assuming these values had been properly configured, these calls would result MyStackedConfig.current() # False MyStackedConfig.current(site=Site(domain='edx.org')) # True MyStackedConfig.current(site=Site(domain='whitelabel.edx.org')) # False -- value derived from global setting MyStackedConfig.current(org='HarvardX') # False MyStackedConfig.current(org='MITx') # True -- value derived from edx.org site MyStackedConfig.current(org_course='HarvardX/CS50') # True MyStackedConfig.current(org_course='HarvardX/Bio101') # False -- value derived from HarvardX setting MyStackedConfig.current(course_key=CourseKey(org='HarvardX', course='CS50', run='2018_Q1')) # False MyStackedConfig.current( course_key=CourseKey(org='HarvardX', course='CS50', run='2019_Q1') ) # True -- value derived from HarvardX/CS50 setting bio101 = CourseKey(org='HarvardX', course='Bio101', run='2018_Q1') MyStackedConfig.current(course_key=cs50) # False -- value derived from HarvardX org The following calls would result in errors due to overspecification: MyStackedConfig.current(site=Site(domain='edx.org'), org='HarvardX') MyStackedConfig.current(site=Site(domain='edx.org'), course=cs50) MyStackedConfig.current(org='HarvardX', course=cs50) Arguments: site: The Site to check current values for org: The org to check current values for org_course: The course in a specific org to check current values for course_key: The course to check current values for Returns: An instance of :class:`cls.attribute_tuple()` with the overridden values specified down to the level of the supplied argument (or global values if no arguments are supplied). """ cache_key_name = cls.cache_key_name(site, org, org_course, course_key) cached = cache.get(cache_key_name) if cached is not None: return cached # Raise an error if more than one of site/org/course are specified simultaneously. if len([arg for arg in [site, org, org_course, course_key] if arg is not None]) > 1: raise ValueError("Only one of site, org, org_course, and course can be specified") if org_course is None and course_key is not None: org_course = cls._org_course_from_course_key(course_key) if org is None and org_course is not None: org = cls._org_from_org_course(org_course) if site is None and org is not None: site = cls._site_from_org(org) stackable_fields = [cls._meta.get_field(field_name) for field_name in cls.STACKABLE_FIELDS] field_defaults = { field.name: field.get_default() for field in stackable_fields } values = field_defaults.copy() global_override_q = Q(site=None, org=None, org_course=None, course_id=None) site_override_q = Q(site=site, org=None, org_course=None, course_id=None) org_override_q = Q(site=None, org=org, org_course=None, course_id=None) org_course_override_q = Q(site=None, org=None, org_course=org_course, course_id=None) course_override_q = Q(site=None, org=None, org_course=None, course_id=course_key) overrides = cls.objects.current_set().filter( global_override_q | site_override_q | org_override_q | org_course_override_q | course_override_q ) provenances = defaultdict(lambda: Provenance.default) # We are sorting in python to avoid doing a filesort in the database for # what will only be 4 rows at maximum def sort_key(override): """ Sort overrides in increasing specificity. This particular sort order sorts None before not-None (because False < True) It sorts global first (because all entries are None), then site entries (because course_id and org are None), then org, org_course and course (by the same logic) """ return ( override.course_id is not None, override.org_course is not None, override.org is not None, override.site_id is not None ) for override in sorted(overrides, key=sort_key): for field in stackable_fields: value = field.value_from_object(override) if value != field_defaults[field.name]: values[field.name] = value if override.course_id is not None: provenances[field.name] = Provenance.run elif override.org_course is not None: provenances[field.name] = Provenance.org_course elif override.org is not None: provenances[field.name] = Provenance.org elif override.site_id is not None: provenances[field.name] = Provenance.site else: provenances[field.name] = Provenance.global_ current = cls(**values) current.provenances = {field.name: provenances[field.name] for field in stackable_fields} # pylint: disable=attribute-defined-outside-init cache.set(cache_key_name, current, cls.cache_timeout) return current