Skip to content

Commit 1df3f38

Browse files
committed
fix: add generate_from_sql and deprecate generateFromSql
1 parent d25216a commit 1df3f38

File tree

3 files changed

+106
-80
lines changed

3 files changed

+106
-80
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
django_query_to_table/__pycache__/*
2+
django_query_to_table.egg-info/*

README.md

+2-3
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,14 @@ pip install django-query-to-table
3030

3131
## Usage :
3232

33-
33+
- Generate html table by SQL query
3434
```python
3535
from django.db import connection
3636
from django_query_to_table import DjangoQtt
3737
from django.http import HttpResponse
3838

3939
# view function in Django project
4040
def listOfPersons(request):
41-
cursor = connection.cursor()
4241
reportTitle = "Employee List"
4342
sqlQuery = "SELECT FirstName as 'First Name', LastName as 'Last Name', phone as 'Phone Number', salary as 'Salary' FROM persons"
4443
columnsToBeSummarized = ['Salary']
@@ -48,7 +47,7 @@ def listOfPersons(request):
4847
evenRowsBackgroundColor = '#ffeeff'
4948
oddRowsBackgroundColor = '#ffffff'
5049
rowIndexVisibility = True
51-
table = DjangoQtt.generateFromSql(cursor, reportTitle, sqlQuery, columnsToBeSummarized, cssClasses,
50+
table = DjangoQtt.generate_from_sql(reportTitle, sqlQuery, columnsToBeSummarized, cssClasses,
5251
"ltr", fontName, "Total Salary", rowIndexVisibility,
5352
headerRowBackgroundColor, evenRowsBackgroundColor, oddRowsBackgroundColor
5453
)

django_query_to_table/DjangoQtt.py

+102-77
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,105 @@
1+
import traceback
2+
import warnings
13
from django.template import Template, Context
4+
from django.db import connection
5+
import logging
26

3-
import traceback
7+
logger = logging.getLogger(__name__)
8+
9+
def _validate_sql(sqltext):
10+
"""Validate the SQL query."""
11+
if len(sqltext) < 8 or ("select" not in sqltext.lower()):
12+
return False
13+
return True
14+
15+
def _execute_query(cursor, sqltext):
16+
"""Execute the SQL query and fetch data."""
17+
cursor.execute(sqltext)
18+
desc = cursor.description
19+
result_as_list = [
20+
dict(zip([col[0] for col in desc], row)) for row in cursor.fetchall()
21+
]
22+
columns = [col[0] for col in desc]
23+
return columns, result_as_list
24+
25+
def _calculate_sums(data, columns, footerCols, totalText):
26+
"""Calculate sum for specified columns."""
27+
sumOfColumn = {col: 0 if col in footerCols else "-" for col in columns}
28+
29+
for d in data:
30+
for attr, value in d.items():
31+
if attr in footerCols:
32+
sumOfColumn[attr] += float(str(value).replace(",", ""))
33+
34+
totalColumnSet = False
35+
for col in sumOfColumn:
36+
if sumOfColumn[col] != "-":
37+
sumOfColumn[col] = format(float(str(sumOfColumn[col])), ",")
38+
elif not totalColumnSet:
39+
sumOfColumn[col] = totalText
40+
totalColumnSet = True
41+
42+
return sumOfColumn
43+
44+
def _render_template(context):
45+
"""Render the HTML table template."""
46+
template = (
47+
"<center><table dir=\"{{direction}}\" border=\"1\" class=\"table table-striped {{htmlClass}}\" "
48+
"style=\"width:93%;font-family:'{{font}}'\">"
49+
" <thead> <tr> <th colspan='{{columns|length|add:'1'}}' style=\"font-family:'{{font}}';font-weight: bold;\" > {{title}} </th> </tr>"
50+
" <tr style='background-color:{{headerRowColor}}'>{% if rowIndex %} <th align=\"center\"> # </th> {% endif %} {% for c in columns %} <th>{{ c }}</th> {% endfor %} </tr> </thead>"
51+
" <tbody> {% for d in data %} <tr style='background-color:{% if forloop.counter0|divisibleby:'2' %} {{evenRowColor}} {% else %} {{oddRowColor}} {% endif %} ' >"
52+
" {% if rowIndex %} <td align=\"center\">{{ forloop.counter }}</td> {% endif %} {% for attr, value in d.items %} <td align=\"center\">{{ value }}</td> {% endfor %} </tr> {% endfor %} "
53+
" {% if sumOfColumn %} <tr style='background-color:#eee;font-weight: bold;'> <td></td> {% for a,v in sumOfColumn.items %} <td align=\"center\">{{ v }}</td> {% endfor %} </tr> {% endif %}</tbody> </table></center>"
54+
)
55+
56+
return Template(template).render(Context(context))
57+
58+
def _generate_report(cursor, title, sqltext, footerCols, htmlClass, direction, font, totalText, rowIndex, headerRowColor, evenRowColor, oddRowColor):
59+
"""Internal method to generate the SQL report."""
60+
columns, data = _execute_query(cursor, sqltext)
61+
62+
sumOfColumn = None
63+
if footerCols:
64+
sumOfColumn = _calculate_sums(data, columns, footerCols, totalText)
65+
66+
context = {
67+
'title': title,
68+
'data': data,
69+
'columns': columns,
70+
'sumOfColumn': sumOfColumn,
71+
'direction': direction,
72+
'font': font,
73+
'totalText': totalText,
74+
'rowIndex': rowIndex,
75+
'headerRowColor': headerRowColor,
76+
'evenRowColor': evenRowColor,
77+
'oddRowColor': oddRowColor,
78+
'htmlClass': htmlClass
79+
}
80+
81+
return _render_template(context)
82+
83+
def generate_from_sql(title, sqltext, footerCols=None, htmlClass="", direction="ltr", font="Tahoma", totalText="Total", rowIndex=False, headerRowColor='#eeeeee', evenRowColor='#ffffff', oddRowColor="#ffffff"):
84+
"""Generate the SQL report with an internally created cursor."""
85+
try:
86+
if not _validate_sql(sqltext):
87+
return 'Not Valid SQL'
88+
89+
with connection.cursor() as cursor:
90+
return _generate_report(cursor, title, sqltext, footerCols, htmlClass, direction, font, totalText, rowIndex, headerRowColor, evenRowColor, oddRowColor)
91+
except Exception as e:
92+
logger.error(traceback.format_exc())
93+
return f"Error: {str(e)}"
94+
95+
def generateFromSql(cursor, title, sqltext, footerCols=None, htmlClass="", direction="ltr", font="Tahoma", totalText="Total", rowIndex=False, headerRowColor='#eeeeee', evenRowColor='#ffffff', oddRowColor="#ffffff"):
96+
"""Generate the SQL report with a provided cursor."""
97+
warnings.warn("generateFromSql is deprecated and will be removed in a future release. Use generate_from_sql instead.", DeprecationWarning)
98+
try:
99+
if not _validate_sql(sqltext):
100+
return 'Not Valid SQL'
4101

5-
def generateFromSql(cursor, title, sqltext, footerCols= None, htmlClass="", direction="ltr", font="Tahoma", totalText = "Total", rowIndex = False, headerRowColor ='#eeeeee' ,evenRowColor = '#ffffff', oddRowColor="#ffffff") :
6-
sumCols=[]
7-
try :
8-
if(len(sqltext) < 8 or ("select" not in sqltext.lower())) :
9-
return ('Not Valid SQL')
10-
11-
sql_query = sqltext
12-
sumCols=footerCols
13-
# execute sql query and retrieve data from db
14-
cursor.execute(sql_query)
15-
16-
# retrieve columns of the data
17-
desc = cursor.description
18-
19-
result_as_list = [
20-
dict(zip([col[0] for col in desc ], row)) for row in cursor.fetchall()
21-
]
22-
23-
columns = [col[0] for col in desc ] #result.keys()
24-
data = result_as_list
25-
26-
27-
28-
29-
30-
sumOfColumn = None
31-
if(sumCols != None) :
32-
sumOfColumn={}
33-
34-
# initiating footer aggrigation values
35-
for c in columns :
36-
if(c in sumCols):
37-
sumOfColumn[c]=0
38-
else:
39-
sumOfColumn[c]="-"
40-
41-
# travers in rows and aggrigate the values of columns those are in footerCols
42-
for d in data :
43-
for attr, value in dict(d).items() :
44-
if(attr in sumCols):
45-
sumOfColumn[attr]=sumOfColumn[attr]+float(str(value).replace(",", ""))
46-
47-
totalColumnSet = False
48-
if(sumCols != None) :
49-
for col, val in sumOfColumn.items() :
50-
if(val!="-") :
51-
sumOfColumn[col] = format(float(str(val)),",")
52-
elif (totalColumnSet == False) :
53-
sumOfColumn[col] = totalText
54-
totalColumnSet = True
55-
56-
# template to generate data from data retrieved from database
57-
template= "<center><table dir=\"{{direction}}\" border=\"1\" class=\"table table-striped {{htmlClass}}\" style=\"width:93%;font-family:'{{font}}'\"> <thead> <tr> <th colspan='{{columns|length|add:'1'}}' style=\"font-family:'{{font}}';font-weight: bold;\" > {{title}} </th> </tr> <tr style='background-color:{{headerRowColor}}'>{% if rowIndex == True %} <th align=\"center\"> # </th> {% endif %} {% for c in columns %} <th>{{ c }}</th> {% endfor %} </tr> </thead> <tbody> {% for d in data %} <tr style='background-color:{% if forloop.counter0|divisibleby:'2' %} {{evenRowColor}} {% else %} {{oddRowColor}} {% endif %} ' > {% if rowIndex == True %} <td align=\"center\">{{ forloop.counter }}</td> {% endif %} {% for attr, value in d.items %} <td align=\"center\">{{ value }}</td> {% endfor %} </tr> {% endfor %} {% if sumOfColumn != None %} <tr style='background-color:#eee;font-weight: bold;'> <td></td> {% for a,v in sumOfColumn.items %} <td align=\"center\">{{ v }}</td> {% endfor %} </tr> {% endif %}</tbody> </table></center>"
58-
59-
60-
61-
c = Context({
62-
'title':title,
63-
'data':data,
64-
'columns':columns,
65-
'sumOfColumn':sumOfColumn,
66-
'direction':direction,
67-
'font':font,
68-
'totalText':totalText,
69-
'rowIndex' : rowIndex,
70-
'headerRowColor' :headerRowColor ,
71-
'evenRowColor' : evenRowColor,
72-
'oddRowColor': oddRowColor,
73-
'htmlClass' : htmlClass
74-
})
75-
76-
return Template(template).render(c)
77-
except BaseException as e :
78-
#print exception trace to console
79-
print(traceback.format_exc())
80-
return ("Error :" + str(e))
102+
return _generate_report(cursor, title, sqltext, footerCols, htmlClass, direction, font, totalText, rowIndex, headerRowColor, evenRowColor, oddRowColor)
103+
except Exception as e:
104+
logger.error(traceback.format_exc())
105+
return f"Error: {str(e)}"

0 commit comments

Comments
 (0)