appsembler / figures

Reporting and data retrieval app for Open edX
MIT License
44 stars 37 forks source link

[Proposal] Abstracting Figures site management into plugins #416

Closed OmarIthawi closed 1 year ago

OmarIthawi commented 2 years ago

Timing of this post

I was checking the our collaboration with Sofiane to upgrade to Maple and I like the idea of starting with a clean branch for Maple.

I think it's a good time to discuss the multi-site backend in Figures which can ublock Maple since we don't have the edx-organizations for ready for Maple, nor we plan to.

The current plan is to use the new appsembler/tahoe-sites package that extends a vanilla edx-organizations package to avoid forking the upstream package.

Shadi's working on refactoring our changes out of the edx-organizations package into tahoe-sites.

This is not required for the Maple release. However, it looks like now is the right time for such architectural change.

It's not new that we have the idea of introducing plugins into Figures and making the sites backends less hardcoded.

This issue outlines a specific idea to make that possible that aligns with both the upcoming Koa upgrade, the tahoe-sites work and the community upgrade of Figures to support Maple.

Create three plugins for site handling

Introduce a new setting FIGURES['SITES_MANAGER'] (there might be a better name). This setting will be point to a class that has specific methods to manage sites. Let's start with one example:

Vanilla Open edX single-site manager

By default this manager would be used: FIGURES['SITES_MANAGER'] = 'figures.site_managers.single_site:SingleSiteManager'.

The class could look like the following:

# Pesudo Python code.

class SingleSiteManager:
  def is_multisite():  # Useful for frontend and cosemetics.
    return False

  def get_default_site():
    return Site.objects.get(pk=settings.SITE_ID)

  def get_site_for_course():
    return self.get_default_site()

  def get_courses_for_site(site):
    return CourseOverview.objects.all()

  def get_users_for_site():
    return get_user_model().objects.all()

Vanilla Open edX multi-site manager

This is a new mode in Figures that's not suitable for Tahoe. It will embrace the lightweight Open edX multi-site without having security or user separation.

This manager makes use of both the vanilla edx-organizations and the SiteConfiguration model in upstream Open edX.

We'll probably need to be creative in how the sites are linked to organizations and how to know which users are associated with which site.

Here's how it may look:

# Pesudo Python code.

def get_site_by_org(org_short_name)
   # Omar: The default "heuristic" for Open edX. It works, but it's very bad.
   site_config = SiteConfiguration.objects.get(
      site_values__contains='"course_org_filter": "{}"'.format(course_org),
   )
   return site_config.site

def get_org_by_site(site):
   config = site.configuration
   org_name = config.get_value('course_org_filter')
   return Organizations.objects.get(short_name=org_name)

class MultiSiteManager:
  """Default Open edX multi-site manager."""
  def is_multisite():  # Useful for frontend and cosemetics.
    return True

  def get_default_site():
    return Site.objects.get(pk=settings.SITE_ID)

  def get_site_for_course(course):
    return get_site_by_org(org_short_name=course.id.org)

  def get_course_ids_for_site(site):
    org = get_org_by_site(site)
    return OrganizationCourse.objects.filter(organization=org).values('...')
    return CourseOverview.objects.filter(id__in=course_ids)

  def get_courses_for_site(site):
    return CourseOverview.objects.filter(
      id__in=self.get_course_ids_for_site(site),
    )

  def get_users_for_site(site):
    # Trying to come up with a way without using the Tahoe fork.
    course_ids = get_course_ids_for_site(site)
    enrolled_user_ids = CourseEnrollment.objects.filter(
      course_id__in=course_ids,
    ).values('user_id')
    return get_user_model().objects.filter(
      id__in=enrolled_user_ids,
    )

Tahoe-specific Open edX multi-site manager

This manager will be Tahoe-owned and has no expectation of being used outside Tahoe -- therefore more freedom in changing the behavior and less maintenance restrictions.

This manager will effectively will maintain the same logic that's currently embedded into Figures sites.py module under the if is_multisite(): branches.

This Tahoe class will live in the new appsembler/tahoe-figures-plugins repository which will host Tahoe-specific customization we have for Figures in Tahoe.

Circling back on timing and prioritization

[Omar] I'm writing this page in Jan 26, 2022, and this idea isn't really in the Sprint 101 Priorities.

However, Sofiane will get blocked on the Maple release because we the Tahoe appsembler/edx-organizations fork isn't on Maple yet and the plan is to decustomize it starting from Juniper and Koa like we're doing with the Course Access Groups: