Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verify card template sql api added #49

Merged
merged 13 commits into from
Feb 28, 2022
33 changes: 28 additions & 5 deletions api/cueSearch/services/cardTemplate.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import json
import logging
from utils.apiResponse import ApiResponse
from cueSearch.serializers import SearchCardTemplateSerializer
from cueSearch.models import SearchCardTemplate
from dataset.models import ConnectionType
from django.template import Template, Context
from cueSearch.services.searchCardTemplate import SearchCardTemplateServices
from cueSearch.services.sampleParams import SAMPLE_PARAMS

logger = logging.getLogger(__name__)

class CardTemplates:
"""
Expand All @@ -16,7 +21,7 @@ def createCardTemplate(payload: dict):
Create search card template
"""
try:
res = ApiResponse("Error occur while creating search card template")
res = ApiResponse("Error occurred while creating search card template")
connectionTypeId = int(payload.get("connectionTypeId", 1))
connectionType = ConnectionType.objects.get(id=connectionTypeId)
renderType = payload.get("renderType", "table")
Expand All @@ -36,7 +41,7 @@ def createCardTemplate(payload: dict):
res.update(True, "Search card template created successfully")
except Exception as ex:
logging.error("Error %s", str(ex))
res.update(False, "Exception occured while creating templates")
res.update(False, "Exception occurred while creating templates")
return res

@staticmethod
Expand Down Expand Up @@ -96,7 +101,7 @@ def publishedCardTemplate(payload: dict):
res.update(True, "Card Template published successfully")
except Exception as ex:
logging.error("Error %s", str(ex))
res.update(False, "Error occured while publishing Card Template")
res.update(False, "Error occurred while publishing Card Template")
return res

def deleteCardTemplate(templateId: int):
Expand All @@ -108,7 +113,7 @@ def deleteCardTemplate(templateId: int):
res.update(True, "Card template deleted successfully")
except Exception as ex:
logging.error("Error while deleting %s", str(ex))
res.update(False, "Error occured while deleting card template")
res.update(False, "Error occurred while deleting card template")
return res

@staticmethod
Expand All @@ -121,5 +126,23 @@ def getCardTemplateById(templateId: int):
res.update(True, "Fetched card templates", data)
except Exception as ex:
logging.error("Error while get card template by Id %s", str(ex))
res.update(False, "Error occured while getting template by id")
res.update(False, "Error occurred while getting template by id")
return res

@staticmethod
def verifyCardTemplate(payload: dict):
res = ApiResponse()
try:
sampleParams = json.loads(json.dumps(SAMPLE_PARAMS))
param = {
**sampleParams,
"templateTitle": payload['templateTitle'],
"templateText": payload['templateText'],
"templateSql": payload['templateSql'],
}
response = SearchCardTemplateServices.renderTemplatesUnsafe(param)
res.update(True,"Template rendered successfully")
except Exception as ex:
logger.error("Error in rendering templates: %s", str(ex))
res.update(False,"Error occurred during rendering", str(ex))
return res
111 changes: 111 additions & 0 deletions api/cueSearch/services/sampleParams.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
SAMPLE_PARAMS ={
'dataset': 'Orders',
'datasetId': 2,
'datasetSql': "SELECT DATE_TRUNC('DAY', __time) as OrderDate,\n"'Brand, Color, State,\n''SUM("count") as Orders, ROUND(sum(OrderAmount),2) as ''OrderAmount, sum(OrderQuantity) as OrderQuantity\n''FROM FAKEORDERS\n'"WHERE __time >= CURRENT_TIMESTAMP - INTERVAL '13' MONTH \n"'GROUP BY 1, 2, 3, 4\n''ORDER BY 1',
'dimensions': [
'Brand',
'Color',
'State'
],
'filter': "( ( Brand = 'Adidas' OR Brand = 'Nike' ) ) AND ( State = 'MS' OR ""State = 'KS' )",
'filterDimensions': [
'Brand',
'State'
],
'granularity': 'day',
'groupedResultsForFilter': [
[
{
'dataset': 'Orders',
'datasetId': 2,
'dimension': 'Brand',
'globalDimensionName': 'Brand',
'id': 2,
'type': 'GLOBALDIMENSION',
'user_entity_identifier': 'Brand',
'value': 'Adidas'
},
{
'dataset': 'Orders',
'datasetId': 2,
'dimension': 'Brand',
'globalDimensionName': 'Brand',
'id': 2,
'type': 'GLOBALDIMENSION',
'user_entity_identifier': 'Brand',
'value': 'Nike'
}
],
[
{
'dataset': 'Orders',
'datasetId': 2,
'dimension': 'State',
'globalDimensionName': 'Region',
'id': 1,
'type': 'GLOBALDIMENSION',
'user_entity_identifier': 'Region',
'value': 'MS'
},
{
'dataset': 'Orders',
'datasetId': 2,
'dimension': 'State',
'globalDimensionName': 'Region',
'id': 1,
'type': 'GLOBALDIMENSION',
'user_entity_identifier': 'Region',
'value': 'KS'
}
]
],
'metrics': [
'Orders',
'OrderAmount',
'OrderQuantity'
],
'renderType': 'line',
'searchResults': [
{
'dataset': 'Orders',
'datasetId': 2,
'dimension': 'State',
'globalDimensionName': 'Region',
'id': 1,
'type': 'GLOBALDIMENSION',
'user_entity_identifier': 'Region',
'value': 'MS'
},
{
'dataset': 'Orders',
'datasetId': 2,
'dimension': 'State',
'globalDimensionName': 'Region',
'id': 1,
'type': 'GLOBALDIMENSION',
'user_entity_identifier': 'Region',
'value': 'KS'
},
{
'dataset': 'Orders',
'datasetId': 2,
'dimension': 'Brand',
'globalDimensionName': 'Brand',
'id': 2,
'type': 'GLOBALDIMENSION',
'user_entity_identifier': 'Brand',
'value': 'Adidas'
},
{
'dataset': 'Orders',
'datasetId': 2,
'dimension': 'Brand',
'globalDimensionName': 'Brand',
'id': 2,
'type': 'GLOBALDIMENSION',
'user_entity_identifier': 'Brand',
'value': 'Nike'
}
],
'timestampColumn': 'OrderDate'
}
55 changes: 33 additions & 22 deletions api/cueSearch/services/searchCardTemplate.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,35 +191,46 @@ def renderTemplates(param: dict):
returns: [{ title: str, text: str, sql: str }]
"""
response = []
delimiter = "+-;"
try:
titles = (
Template(param["templateTitle"]).render(Context(param)).split(delimiter)
)
texts = (
Template(param["templateText"]).render(Context(param)).split(delimiter)
)
sqls = (
Template(param["templateSql"]).render(Context(param)).split(delimiter)
)

if len(titles) != len(texts) or len(titles) != len(sqls):
raise ValueError(
"Inconsistent use of delimiter (%s) in title, text, sql of template"
% delimiter
)

for i in range(len(sqls)):
if str.isspace(sqls[i]):
continue
response.append({"title": titles[i], "text": texts[i], "sql": sqls[i]})

response = SearchCardTemplateServices.renderTemplatesUnsafe(param)
except Exception as ex:
logger.error("Error in rendering templates: %s", str(ex))
logger.error(param)

return response

@staticmethod
def renderTemplatesUnsafe(param: dict):
"""
Renders template with passed variables, without error handling
:param param: dict with values needed for rendering
returns: [{ title: str, text: str, sql: str }]
"""
response = []
delimiter = "+-;"
titles = (
Template(param["templateTitle"]).render(Context(param)).split(delimiter)
)
texts = (
Template(param["templateText"]).render(Context(param)).split(delimiter)
)
sqls = (
Template(param["templateSql"]).render(Context(param)).split(delimiter)
)
if len(titles) != len(texts) or len(titles) != len(sqls):
raise ValueError(
"Inconsistent use of delimiter (%s) in title, text, sql of template"
% delimiter
)

for i in range(len(sqls)):
if str.isspace(sqls[i]):
continue
response.append({"title": titles[i], "text": texts[i], "sql": sqls[i]})

return response


@staticmethod
def getSearchSuggestions(query):
"""Get searchsuggestion for search dropdown"""
Expand Down
67 changes: 62 additions & 5 deletions api/cueSearch/tests/test_cardTemplate.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
from unittest import mock
from django.urls import reverse
from mixer.backend.django import mixer

Expand All @@ -19,14 +20,14 @@ def testCardTemplates(client, mocker):
"connectionTypeId": 1,
}
response = client.post(path, payload, content_type="application/json")
assert response.data["success"]
assert response.status_code == 200
assert response.data["success"]

# Getting the existing templates
path = reverse("getTemplates")
response = client.get(path, content_type="application/json")
assert response.data["success"]
assert response.status_code == 200
assert response.data["success"]
response.json()["data"][0]["templateName"] == "Sample"
response.json()["data"][0]["published"] == False
id = response.json()["data"][0]["id"]
Expand All @@ -35,14 +36,14 @@ def testCardTemplates(client, mocker):
payload = {"id": id, "published": False}
path = reverse("pubCardTemplates")
response = client.post(path, payload, content_type="application/json")
assert response.data["success"]
assert response.status_code == 200
assert response.data["success"]

# Getting the template by Id
path = reverse("getTemplatesById", kwargs={"id": id})
response = client.get(path, content_type="application/json")
assert response.data["success"]
assert response.status_code == 200
assert response.data["success"]

# Updating the card template
payload = {
Expand All @@ -59,13 +60,69 @@ def testCardTemplates(client, mocker):
# Getting the existing templates
path = reverse("getTemplates")
response = client.get(path, content_type="application/json")
assert response.data["success"]
assert response.status_code == 200
assert response.data["success"]
response.json()["data"][0]["templateName"] == "updatedSample"
response.json()["data"][0]["title"] == "updatedSample"

# Deleting the existing templates
path = reverse("cardTemplateDelete", kwargs={"id": id})
response = client.delete(path, payload, content_type="application/json")
assert response.status_code == 200
assert response.data["success"]




@pytest.mark.django_db(transaction=True)
def testVerifyCardTemplates(client, mocker):

templateTitle = "{% load event_tags %} {% for filterDim in filterDimensions %} {% conditionalCount searchResults 'dimension' filterDim as dimCount %} {% if dimCount > 1 %} {% for metricName in metrics %} Comparison of <span style=\"background:#eee; padding: 0 4px; border-radius: 4px;\">{{metricName}}</span> among <span style=\"background:#eee; padding: 0 4px; border-radius: 4px;\">{{filterDim}}</span> values in <span style=\"background:#eee; padding: 0 4px; border-radius: 4px;\">{{dataset}}</span> +-; {% endfor %} {% endif %} {% endfor %}"
templateText = "{% load event_tags %} {% for filterDim in filterDimensions %} {% conditionalCount searchResults 'dimension' filterDim as dimCount %} {% if dimCount > 1 %} {% for metricName in metrics %} This chart displays filtered values on dimension <span style=\"background:#eee; padding: 0 4px; border-radius: 4px;\">{{filterDim}}</span> along with other filters applied i.e. <span style=\"background:#eee; padding: 0 4px; border-radius: 4px;\">{{filter|safe}}</span> for metric <span style=\"background:#eee; padding: 0 4px; border-radius: 4px;\">{{metricName}}</span> on dataset <span style=\"background:#eee; padding: 0 4px; border-radius: 4px;\">{{dataset}}</span> +-; {% endfor %} {% endif %} {% endfor %}"
templateSql = "{% load event_tags %} {% for filterDim in filterDimensions %} {% conditionalCount searchResults 'dimension' filterDim as dimCount %} {% if dimCount > 1 %} {% for metricName in metrics %} SELECT \"templatetable\".\"{{ timestampColumn }}\", \"templatetable\".\"{{ filterDim }}\", SUM(\"templatetable\".\"{{ metricName }}\") as {{metricName}} FROM ({{ datasetSql|safe }}) AS templatetable WHERE {% for orResults in groupedResultsForFilter %} {% for orResult in orResults %} \"templatetable\".\"{{ orResult.dimension }}\" = '{{ orResult.value }}' OR {% endfor %} True AND {% endfor %} True GROUP BY 1, 2 limit 500 +-; {% endfor %} {% endif %} {% endfor %}"
noVariableTemplateSql = "{% load event_tags %} {% for filterDim in filterDimensionx %} {% conditionalCount searchResults 'dimension' filterDim as dimCount %} {% if dimCount > 1 %} {% for metricName in metrics %} SELECT \"templatetable\".\"{{ timestampColumn }}\", \"templatetable\".\"{{ filterDim }}\", SUM(\"templatetable\".\"{{ metricName }}\") as {{metricName}} FROM ({{ datasetSql|safe }}) AS templatetable WHERE {% for orResults in groupedResultsForFilter %} {% for orResult in orResults %} \"templatetable\".\"{{ orResult.dimension }}\" = '{{ orResult.value }}' OR {% endfor %} True AND {% endfor %} True GROUP BY 1, 2 limit 500 +-; {% endfor %} {% endif %} {% endfor %}"

# Testing the card templete api
path = reverse("verifyCardTemplates")
payload = {
"templateTitle": templateTitle,
"templateText": templateText,
"templateSql": templateSql,
}
response = client.post(path, payload, content_type="application/json")
assert response.status_code == 200
assert response.data["success"] == True


path = reverse("verifyCardTemplates")
payload = {
"templateTitle": templateTitle,
"templateText": templateText,
"templateSql": noVariableTemplateSql,
}
response = client.post(path, payload, content_type="application/json")
assert response.status_code == 200
assert response.data["success"] == False

# different number of values returning from templateText vs templateSql
path = reverse("verifyCardTemplates")
payload = {
"templateTitle": templateTitle,
"templateText": "",
"templateSql": noVariableTemplateSql,
}
response = client.post(path, payload, content_type="application/json")
assert response.status_code == 200
assert response.data["success"] == False

# empty values for templateText and templateSql resulting to valid template
path = reverse("verifyCardTemplates")
payload = {
"templateTitle": "",
"templateText": "",
"templateSql": "",
}
response = client.post(path, payload, content_type="application/json")
assert response.status_code == 200
assert response.data["success"] == True

1 change: 1 addition & 0 deletions api/cueSearch/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,5 @@
name="cardTemplateDelete",
),
path("templates/publish/", views.pubCardTemplate, name="pubCardTemplates"),
path("templates/verify/", views.verifyCardTemplate, name="verifyCardTemplates"),
]
8 changes: 8 additions & 0 deletions api/cueSearch/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,11 @@ def pubCardTemplate(request: HttpRequest) -> Response:
payload = request.data
res = CardTemplates.publishedCardTemplate(payload)
return Response(res.json())

@api_view(['POST'])
def verifyCardTemplate(request: HttpRequest) -> Response:
"""Method to verify sql"""
payload = request.data
res = CardTemplates.verifyCardTemplate(payload)
return Response(res.json())

Loading