Skip to content

Commit

Permalink
Merge branch 'master' into jls/b5-web-apps
Browse files Browse the repository at this point in the history
  • Loading branch information
minhaminha committed Jul 9, 2024
2 parents a0e6b1a + 1a70089 commit 16a19f0
Show file tree
Hide file tree
Showing 93 changed files with 2,485 additions and 761 deletions.
4 changes: 0 additions & 4 deletions corehq/apps/accounting/static/accounting/js/lib/stripe.js

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ hqDefine('accounting/js/payment_method_handler', [
'jquery',
'knockout',
'underscore',
'accounting/js/lib/stripe',
'stripe',
], function (
$,
ko,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
hqDefine("accounting/js/stripe_card_manager", [
'jquery',
'knockout',
'accounting/js/lib/stripe',
'stripe',
], function (
$,
ko,
Expand Down
61 changes: 22 additions & 39 deletions corehq/apps/accounting/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,6 @@
from corehq.util.serialization import deserialize_decimal
from corehq.util.soft_assert import soft_assert

_invoicing_complete_soft_assert = soft_assert(
to=[
'{}@{}'.format(name, 'dimagi.com')
for name in ['gbova', 'dmore', 'accounts']
],
exponential_backoff=False,
)


@transaction.atomic
def _activate_subscription(subscription):
Expand Down Expand Up @@ -283,6 +275,18 @@ def check_credit_line_balances():
)


_invoicing_error_soft_assert = soft_assert(
to=settings.ACCOUNTS_EMAIL,
exponential_backoff=False,
)


def log_error_and_soft_assert(error_message):
log_accounting_error(error_message, show_stack_trace=True)
if not settings.UNIT_TESTING:
_invoicing_error_soft_assert(False, error_message)


def generate_invoices_based_on_date(invoice_date):
invoice_start, invoice_end = get_previous_month_date_range(invoice_date)
log_accounting_info("Starting up invoices for %(start)s - %(end)s" % {
Expand All @@ -299,22 +303,13 @@ def generate_invoices_based_on_date(invoice_date):
invoice_factory.create_invoices()
log_accounting_info("Sent invoices for domain %s" % domain_obj.name)
except CreditLineError as e:
log_accounting_error(
"There was an error utilizing credits for "
"domain %s: %s" % (domain_obj.name, e),
show_stack_trace=True,
)
log_error_and_soft_assert("There was an error utilizing credits for "
"domain %s: %s" % (domain_obj.name, e))
except InvoiceError as e:
log_accounting_error(
"Could not create invoice for domain %s: %s" % (domain_obj.name, e),
show_stack_trace=True,
)
log_error_and_soft_assert("Could not create invoice for domain %s: %s" % (domain_obj.name, e))
except Exception as e:
log_accounting_error(
"Error occurred while creating invoice for "
"domain %s: %s" % (domain_obj.name, e),
show_stack_trace=True,
)
log_error_and_soft_assert("Error occurred while creating invoice for "
"domain %s: %s" % (domain_obj.name, e))
all_customer_billing_accounts = BillingAccount.objects.filter(is_customer_billing_account=True)
for account in all_customer_billing_accounts:
try:
Expand All @@ -331,25 +326,13 @@ def generate_invoices_based_on_date(invoice_date):
)
invoice_factory.create_invoice()
except CreditLineError as e:
log_accounting_error(
"There was an error utilizing credits for "
"domain %s: %s" % (domain_obj.name, e),
show_stack_trace=True,
)
log_error_and_soft_assert("There was an error utilizing credits for "
"domain %s: %s" % (domain_obj.name, e))
except InvoiceError as e:
log_accounting_error(
"Could not create invoice for domain %s: %s" % (domain_obj.name, e),
show_stack_trace=True,
)
log_error_and_soft_assert("Could not create invoice for domain %s: %s" % (domain_obj.name, e))
except Exception as e:
log_accounting_error(
"Error occurred while creating invoice for "
"domain %s: %s" % (domain_obj.name, e),
show_stack_trace=True,
)

if not settings.UNIT_TESTING:
_invoicing_complete_soft_assert(False, "Invoicing is complete!")
log_error_and_soft_assert("Error occurred while creating invoice for "
"domain %s: %s" % (domain_obj.name, e))


@periodic_task(run_every=crontab(hour=13, minute=0, day_of_month='1'), acks_late=True)
Expand Down
7 changes: 7 additions & 0 deletions corehq/apps/analytics/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,10 @@ Generally available in areas of interest to the product and growth teams: signup
### Facebook Pixel

Their script is included in [signup](https://github.com/dimagi/commcare-hq/blob/master/corehq/apps/registration/templates/registration/register_new_user.html), but we don't do any event tracking or other interaction with it. Very little related code, just the script inclusion.

### Google Tag Manager (GTM)

Its script is available in the [gtm.js](https://github.com/dimagi/commcare-hq/blob/master/corehq/apps/analytics/static/analytix/js/gtm.js) which loads the GTM tracking script and sends the desired user properties to GTM.
Any tracking of events should be configured at the GTM console end in tandem with the desired analytics tooling. The goal is to track specific features in HQ and also disable them when there is no need via the GTM console itself.
Any `id` attribute added to html element for tracking through console should be prefixed with `gtm-` to indicate that this element is likely being tracked in GTM.
This should potentially avoid accidental removal of id attribute from these elements. (Similar approach may be followed for any tooling in case of tracking of elements through console.)
68 changes: 68 additions & 0 deletions corehq/apps/analytics/static/analytix/js/gtm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"use strict";
/**
* Handles communication with the google tag manager API.
*/
hqDefine('analytix/js/gtm', [
'jquery',
'underscore',
'analytix/js/initial',
'analytix/js/logging',
'analytix/js/utils',
], function (
$,
_,
initialAnalytics,
logging,
utils
) {
var _get = initialAnalytics.getFn('gtm'),
_logger = logging.getLoggerForApi('Google Tag Manager'),
_ready = $.Deferred();

window.dataLayer = window.dataLayer || [];

/**
* Helper function to send event to Google Tag Manager.
* @param {string} eventName
* @param {object} eventData
* @param {function|undefined} callbackFn - optional
*/
var gtmSendEvent = function (eventName, eventData, callbackFn) {
_ready.done(function () {
var data = {
event: eventName,
};
if (eventData) {
_.extend(data, eventData);
}
window.dataLayer.push(data);
_logger.verbose.log(eventName, 'window.dataLayer.push');
}).fail(function () {
if (_.isFunction(callbackFn)) {
callbackFn();
}
});
};

$(function () {
var apiId = _get('apiId'),
scriptUrl = '//www.googletagmanager.com/gtm.js?id=' + apiId;

_ready = utils.initApi(_ready, apiId, scriptUrl, _logger, function () {
var userProperties = {
userId: _get('userId', 'none'),
isDimagi: _get('userIsDimagi', 'no', 'yes'),
isCommCare: _get('userIsCommCareUser', 'no', 'yes'),
domain: _get('domain', 'none'),
hqEnvironment: _get('hqInstance', 'none'),
};
// userProperties are sent first to be available for use as early as possible
gtmSendEvent('userProperties', userProperties);
gtmSendEvent('gtm.js', {'gtm.start': new Date().getTime()});
});
});

return {
sendEvent: gtmSendEvent,
};
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
<script src="{% static 'analytix/js/hubspot.js' %}"></script>
<script src="{% static 'analytix/js/drift.js' %}"></script>
<script src="{% static 'analytix/js/appcues.js' %}"></script>
<script src="{% static 'analytix/js/gtm.js' %}"></script>
{% endcompress %}
1 change: 1 addition & 0 deletions corehq/apps/analytics/templates/analytics/initial/all.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
{% include 'analytics/initial/kissmetrics.html' %}
{% include 'analytics/initial/hubspot.html' %}
{% include 'analytics/initial/appcues.html' %}
{% include 'analytics/initial/gtm.html' %}
13 changes: 13 additions & 0 deletions corehq/apps/analytics/templates/analytics/initial/gtm.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{% load hq_shared_tags %}
{% initial_analytics_data 'gtm.apiId' ANALYTICS_IDS.GTM_ID %}
{% if request.couch_user %}
{% initial_analytics_data 'gtm.userId' request.couch_user.userID %}
{% initial_analytics_data 'gtm.userIsDimagi' request.couch_user.is_dimagi %}
{% initial_analytics_data 'gtm.userIsCommCareUser' request.couch_user.is_commcare_user %}
{% endif %}
{% if domain %}
{% initial_analytics_data 'gtm.domain' domain %}
{% endif %}
{% if ANALYTICS_CONFIG.HQ_INSTANCE %}
{% initial_analytics_data 'gtm.hqInstance' ANALYTICS_CONFIG.HQ_INSTANCE %}
{% endif %}
6 changes: 6 additions & 0 deletions corehq/apps/app_manager/detail_screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,12 @@ class TimeAgo(FormattedDetailColumn):
SORT_XPATH_FUNCTION = "{xpath}"


@register_format_type('image')
class Image(FormattedDetailColumn):
template_form = 'image'
XPATH_FUNCTION = "cc_case_image"


@register_format_type('distance')
class Distance(FormattedDetailColumn):
XPATH_FUNCTION = \
Expand Down
17 changes: 17 additions & 0 deletions corehq/apps/app_manager/static/app_manager/js/details/column.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
hqDefine("app_manager/js/details/column", function () {
const uiElement = hqImport('hqwebapp/js/bootstrap3/ui-element');
const initialPageData = hqImport('hqwebapp/js/initial_page_data').get;
const microCaseImageName = 'cc_case_image';

return function (col, screen) {
/*
Expand Down Expand Up @@ -364,6 +365,15 @@ hqDefine("app_manager/js/details/column", function () {
self.$format = $('<div/>').append(self.format.ui);
self.$format.find("select").css("margin-bottom", "5px");
self.format.on('change', function () {
if (self.field.val() === microCaseImageName && self.format.val() !== 'image') {
// The field name input was disabled to enforce using the reserved micro image name.
// If the format is no longer an image then the user can edit the field input again
self.field.val('');
self.field.observableVal('');
self.field.ui.find('select').val('').change();
self.field.ui.find('select').prop('disabled', false);
}

self.coordinatesVisible(!_.contains(['address', 'address-popup', 'invisible'], self.format.val()));
// Prevent self from running on page load before init
if (self.format.ui.parent().length > 0) {
Expand Down Expand Up @@ -426,6 +436,13 @@ hqDefine("app_manager/js/details/column", function () {
self.time_ago_extra.value = interval.val();
fireChange();
});
} else if (this.val() === 'image') {
// We are enforcing the reserved field name for the micro image format,
// so don't allow a user to change this
self.field.ui.find('select').val(microCaseImageName).change();
self.field.val(microCaseImageName);
self.field.observableVal(microCaseImageName);
self.field.ui.find('select').prop('disabled', true);
}
}
}).fire('change');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,8 @@ hqDefine("app_manager/js/details/screen", function () {
self.save = function () {
// Only save if property names are valid
var errors = [],
containsTab = false;
containsTab = false,
imageColumnCount = 0;
_.each(self.columns(), function (column) {
column.saveAttempted(true);
if (column.isTab) {
Expand All @@ -504,8 +505,14 @@ hqDefine("app_manager/js/details/screen", function () {
}
} else if (column.showWarning()) {
errors.push(gettext("There is an error in your property name: ") + column.field.value);
} else if (column.format.value === 'image') {
imageColumnCount += 1;
}
});

if (imageColumnCount > 1) {
errors.push(gettext("You can only have one property with the 'Image' format"));
}
if (containsTab) {
if (!self.columns()[0].isTab) {
errors.push(gettext("All properties must be below a tab."));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ hqDefine("app_manager/js/details/utils", function () {
});
}

if (hqImport('hqwebapp/js/toggles').toggleEnabled('VELLUM_CASE_MICRO_IMAGE')) {
formats.push({
value: "image",
label: gettext('Image'),
});
}

return formats;
};

Expand Down
16 changes: 10 additions & 6 deletions corehq/apps/case_search/tests/test_case_search_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ def setUpClass(cls):
super().setUpClass()
cls.user = create_user("admin", "123")
CaseSearchConfig.objects.create(domain=cls.domain, enabled=True)
household_1 = str(uuid.uuid4())
cls.household_1 = str(uuid.uuid4())
case_blocks = [CaseBlock(
case_id=household_1,
case_id=cls.household_1,
case_type='household',
case_name="Villanueva",
create=True,
Expand All @@ -48,10 +48,10 @@ def setUpClass(cls):
update=properties,
index={'parent': IndexAttrs('household', household_id, 'child')} if household_id else None,
) for name, properties, household_id in [
("Jane", {"family": "Villanueva"}, household_1),
("Xiomara", {"family": "Villanueva"}, household_1),
("Alba", {"family": "Villanueva"}, household_1),
("Rogelio", {"family": "de la Vega"}, household_1),
("Jane", {"family": "Villanueva"}, cls.household_1),
("Xiomara", {"family": "Villanueva"}, cls.household_1),
("Alba", {"family": "Villanueva"}, cls.household_1),
("Rogelio", {"family": "de la Vega"}, cls.household_1),
("Jane", {"family": "Ramos"}, None),
]])
case_search_es_setup(cls.domain, case_blocks)
Expand All @@ -77,6 +77,10 @@ def test_basic(self):
case.name for case in res
])

def test_case_id_criteia(self):
res = get_case_search_results(self.domain, ['household'], [SearchCriteria('case_id', self.household_1)])
self.assertItemsEqual(["Villanueva"], [case.name for case in res])

def test_dynamic_property(self):
res = get_case_search_results(self.domain, ['person'], [SearchCriteria('family', 'Ramos')])
self.assertItemsEqual(["Jane"], [case.name for case in res])
Expand Down
3 changes: 3 additions & 0 deletions corehq/apps/case_search/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from corehq import toggles
from corehq.apps.app_manager.dbaccessors import get_app_cached
from corehq.apps.app_manager.util import module_offers_search
from corehq.apps.es import cases as case_es
from corehq.apps.case_search.const import (
CASE_SEARCH_MAX_RESULTS,
COMMCARE_PROJECT,
Expand Down Expand Up @@ -269,6 +270,8 @@ def _apply_filter(self, search_es, criteria):
for xpath in xpaths:
search_es = search_es.filter(self._build_filter_from_xpath(xpath))
return search_es
elif criteria.key == 'case_id':
return search_es.filter(case_es.case_ids(criteria.value))
elif criteria.key == 'owner_id':
if not criteria.is_empty:
return search_es.filter(case_search.owner(criteria.value))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
<% } %>
</div>
<% if (title.length > 0) { %>
<h1 aria-label="<%- title %>" tabindex="0" class="page-title"><%- title %></h1>
<h1 aria-label="<%- title %>" class="page-title"><%- title %></h1>
<% } %>
<% if (sidebarEnabled && description.length > 0) { %>
<div aria-label="<%- description %>" tabindex="0" class="query-description"><%= description %></div>
<div aria-label="<%- description %>" class="query-description"><%= description %></div>
<% } %>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<% if (typeof hint !== "undefined" && hint !== null) { %>
<div class="hq-help" data-bs-toggle="popover">
<a href="#" tabindex="-1" title="<%- text ? text : "" %>" data-bs-content="<%- hint ? hint : "" %>">
<i class="fa fa-question-circle icon-question-sign"></i>
<i class="fa fa-question-circle icon-question-sign" tabindex="0"></i>
</a>
</div>
<% } %>
Expand Down
Loading

0 comments on commit 16a19f0

Please sign in to comment.