Skip to content

Commit

Permalink
feat: add SurveyReportUpload and add send report method (#31431)
Browse files Browse the repository at this point in the history
* feat: add SurveyReportUpload and add send report method

* docs: Update openedx/features/survey_report/management/commands/generate_report.py

Co-authored-by: Maria Grimaldi <[email protected]>

* docs: Update openedx/features/survey_report/models.py

Co-authored-by: Maria Grimaldi <[email protected]>

* refactor: Update openedx/features/survey_report/models.py

Co-authored-by: Maria Grimaldi <[email protected]>

* style: Update openedx/features/survey_report/api.py

Co-authored-by: Maria Grimaldi <[email protected]>

* feat: add migratio file and update status field name

* refactor: rename send report method

* test: fix test errors

* test: add command options

* refactor: simple conditional instead of ok method

* fix: remove useless imports

* fix: use status code instead of status

* feat: add zapier endpoint

* style: solve pylint issues

* feat: add id field to send report data

* refactor: regenerate migration with correct history

* feat: add anonymous site id model

* feat: update zapier url

---------

Co-authored-by: Maria Grimaldi <[email protected]>
Co-authored-by: Alejandro Cardenas <[email protected]>
  • Loading branch information
3 people authored Feb 21, 2023
1 parent 9ed2688 commit 7f90b5d
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 10 deletions.
4 changes: 3 additions & 1 deletion lms/envs/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,8 @@ def get_env_setting(setting):

############## Settings for survey report ##############
SURVEY_REPORT_EXTRA_DATA = ENV_TOKENS.get('SURVEY_REPORT_EXTRA_DATA', {})

SURVEY_REPORT_ENDPOINT = ENV_TOKENS.get('SURVEY_REPORT_ENDPOINT',
'https://hooks.zapier.com/hooks/catch/11595998/3ouwv7m/')
ANONYMOUS_SURVEY_REPORT = False

AVAILABLE_DISCUSSION_TOURS = ENV_TOKENS.get('AVAILABLE_DISCUSSION_TOURS', [])
2 changes: 2 additions & 0 deletions lms/envs/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -674,3 +674,5 @@

############## Settings for survey report ##############
SURVEY_REPORT_EXTRA_DATA = {}
SURVEY_REPORT_ENDPOINT = "https://example.com/survey_report"
ANONYMOUS_SURVEY_REPORT = False
62 changes: 60 additions & 2 deletions openedx/features/survey_report/api.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
"""
Contains the logic to manage survey report model.
"""
import requests

from django.conf import settings
from django.forms.models import model_to_dict

from openedx.features.survey_report.models import SurveyReport
from openedx.features.survey_report.models import (
SurveyReport,
SurveyReportUpload,
SurveyReportAnonymousSiteID,
SURVEY_REPORT_ERROR,
SURVEY_REPORT_GENERATED
)
from openedx.features.survey_report.queries import (
get_course_enrollments,
get_recently_active_users,
get_generated_certificates,
get_registered_learners,
get_unique_courses_offered
)
from .models import SURVEY_REPORT_ERROR, SURVEY_REPORT_GENERATED

MAX_WEEKS_SINCE_LAST_LOGIN: int = 4

Expand Down Expand Up @@ -49,6 +56,57 @@ def generate_report() -> None:
except (Exception, ) as update_report_error:
update_report(survey_report.id, {"state": SURVEY_REPORT_ERROR})
raise Exception(update_report_error) from update_report_error
return survey_report.id


def get_id() -> str:
""" Generate id for the survey report."""
if not settings.ANONYMOUS_SURVEY_REPORT:
return settings.LMS_BASE
return str(SurveyReportAnonymousSiteID.objects.get_or_create()[0].id)


def send_report_to_external_api(report_id: int) -> None:
"""
Send a report to Openedx endpoint and save the response in the SurveyReportUpload model.
endpoint: The value of the setting SURVEY_REPORT_ENDPOINT
content_type: JSON
payload:
- courses_offered: Total number of active unique courses.
- learner: Recently active users with login in some weeks.
- registered_learners: Total number of users ever registered in the platform.
- enrollments: Total number of active enrollments in the platform.
- generated_certificates: Total number of generated certificates.
- extra_data: Extra information that will be saved in the report, E.g: site_name, openedx-release.
- created_at: Date when the report was generated, this date will send with format '%m-%d-%Y %H:%M:%S'
"""
report = SurveyReport.objects.get(id=report_id)

fields = [
"courses_offered",
"learners",
"registered_learners",
"generated_certificates",
"enrollments",
]

data = model_to_dict(report, fields=fields)
data["id"] = get_id()
data["extra_data"] = report.extra_data
data["created_at"] = report.created_at.strftime("%m-%d-%Y %H:%M:%S")

request = requests.post(settings.SURVEY_REPORT_ENDPOINT, json=data)

request.raise_for_status()

SurveyReportUpload.objects.create(
report=report,
status_code=request.status_code,
request_details=request.content
)


def update_report(survey_report_id: int, data: dict) -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from django.core.management.base import BaseCommand, CommandError

from openedx.features.survey_report.api import generate_report
from openedx.features.survey_report.api import generate_report, send_report_to_external_api


class Command(BaseCommand):
Expand All @@ -22,12 +22,25 @@ class Command(BaseCommand):
learners ever registered, and generated certificates.
"""

def handle(self, *_args, **_options):
def add_arguments(self, parser):
parser.add_argument(
'--no-send',
action='store_true',
help='Do not send the report after generated.'
)

def handle(self, *_args, **options):
try:
generate_report()
report = generate_report()
self.stdout.write(self.style.SUCCESS('Survey report has been generated successfully.'))
except Exception as error:
raise CommandError(f'An error has occurred while survey report was generating. {error}') from error

self.stdout.write(
self.style.SUCCESS('Survey report has been generated successfully.')
)
if not options['no_send']:
try:
send_report_to_external_api(report_id=report)
self.stdout.write(self.style.SUCCESS('Survey report has been sent successfully.'))
except Exception as send_error:
raise CommandError(
f'An error has occurred while survey report was sending. {send_error}'
) from send_error
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def test_generate_report(self, mock_get_report_data):
}
mock_get_report_data.return_value = report_test_data
out = StringIO()
call_command('generate_report', stdout=out)
call_command('generate_report', no_send=True, stdout=out)

survey_report = SurveyReport.objects.last()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 3.2.16 on 2023-02-01 15:16

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('survey_report', '0003_add_state_field_and_add_default_values_to_fields'),
]

operations = [
migrations.CreateModel(
name='SurveyReportUpload',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sent_at', models.DateTimeField(auto_now=True, help_text='Date when the report was sent to external api.')),
('status_code', models.IntegerField(help_text='Request status code.')),
('request_details', models.CharField(blank=True, help_text='Information about the send request.', max_length=255, null=True)),
('report', models.ForeignKey(help_text='The report that was sent.', on_delete=django.db.models.deletion.CASCADE, to='survey_report.surveyreport')),
],
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 3.2.16 on 2023-02-10 15:45

from django.db import migrations, models
import uuid


class Migration(migrations.Migration):

dependencies = [
('survey_report', '0004_surveyreportupload'),
]

operations = [
migrations.CreateModel(
name='SurveyReportAnonymousSiteID',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
],
),
]
38 changes: 38 additions & 0 deletions openedx/features/survey_report/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
Survey Report models.
"""

import uuid

from django.db import models
from jsonfield import JSONField

Expand Down Expand Up @@ -58,3 +60,39 @@ class SurveyReport(models.Model):
class Meta:
ordering = ["-created_at"]
get_latest_by = 'created_at'


class SurveyReportUpload(models.Model):
"""
This model stores the result of the POST request made to an external service after generating a survey report.
.. no_pii:
fields:
- sent_at: Date when the report was sent.
- report: The report that was sent.
- status: Request status code.
- request_details: Information about the send request.
"""
sent_at = models.DateTimeField(auto_now=True, help_text="Date when the report was sent to external api.")
report = models.ForeignKey(SurveyReport, on_delete=models.CASCADE, help_text="The report that was sent.")
status_code = models.IntegerField(help_text="Request status code.")
request_details = models.CharField(
max_length=255,
null=True,
blank=True,
help_text="Information about the send request."
)

def is_uploaded(self) -> bool:
return 200 <= self.status_code < 300


class SurveyReportAnonymousSiteID(models.Model):
"""
This model is just to save the identification which will be send to the external API when
the settings ANONYMOUS_SURVEY_REPORT is defined.
.. no_pii:
"""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

0 comments on commit 7f90b5d

Please sign in to comment.