Skip to content

Feat/generate from queryset #1

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

Merged
merged 4 commits into from
Dec 14, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
django_query_to_table/__pycache__/*
django_query_to_table.egg-info/*
45 changes: 37 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -4,11 +4,14 @@

You can read more about this package here : [django query to table package](https://mshaeri.com/blog/generate-html-table-report-from-sql-query-in-django/)

The package contains one function named "generateFromSql" accepting 12 arguments :
The package contains two functions named:
- **generate_from_sql**: Generate HTML table by given SQL query
- **generate_from_queryset**:Generate HTML table by given Django queryset

Parameters:

* cursor : DB cursor
* title : The title of the report that will be shown on top of table
* sqltext : The sql select query to retrieve data
* sqltext/queryset : The sql select query to retrieve data / django queryset
* footerCols : A list of columns name that you want to have Sum of values on footer . Example : ['amount','price']
* htmlClass : Html CSS classes for the table
* direction (default = "ltr") : Indicates direction of the report page. "ltr"- Left to Right , "rtl" - Right to Left
@@ -28,17 +31,16 @@ Run following command to install DjangoQtt :
pip install django-query-to-table
```

## Usage :
## Example usage :

- Generate HTML table by SQL query:

```python
from django.db import connection
from django_query_to_table import DjangoQtt
from django.http import HttpResponse

# view function in Django project
def listOfPersons(request):
cursor = connection.cursor()
reportTitle = "Employee List"
sqlQuery = "SELECT FirstName as 'First Name', LastName as 'Last Name', phone as 'Phone Number', salary as 'Salary' FROM persons"
columnsToBeSummarized = ['Salary']
@@ -48,13 +50,40 @@ def listOfPersons(request):
evenRowsBackgroundColor = '#ffeeff'
oddRowsBackgroundColor = '#ffffff'
rowIndexVisibility = True
table = DjangoQtt.generateFromSql(cursor, reportTitle, sqlQuery, columnsToBeSummarized, cssClasses,
table = DjangoQtt.generate_from_sql(reportTitle, sqlQuery, columnsToBeSummarized, cssClasses,
"ltr", fontName, "Total Salary", rowIndexVisibility,
headerRowBackgroundColor, evenRowsBackgroundColor, oddRowsBackgroundColor
)

# here the table is a string variable contianing the html table showing the query result
# here the table is a string variable containing the html table showing the query result
return HttpResponse(table)

```

- Generate HTML table from querset:

```python
from django_query_to_table import DjangoQtt
from django.http import HttpResponse
from .models import Order
# view function in Django project
def listOfPersons(request):

order_queryset = Order.objects.all().values("customer", "product", "amount")
reportTitle = "Order List"
columnsToBeSummarized = ['amount']
fontName = "Arial"
cssClasses = "reportTable container"
headerRowBackgroundColor = '#ffeeee'
evenRowsBackgroundColor = '#ffeeff'
oddRowsBackgroundColor = '#ffffff'
rowIndexVisibility = True
table = DjangoQtt.generate_from_queryset(reportTitle, order_queryset, columnsToBeSummarized, cssClasses,
"ltr", fontName, "Total amount", rowIndexVisibility,
headerRowBackgroundColor, evenRowsBackgroundColor, oddRowsBackgroundColor
)

# here the table is a string variable containing the html table showing the queryset result
return HttpResponse(table)

```
188 changes: 111 additions & 77 deletions django_query_to_table/DjangoQtt.py
Original file line number Diff line number Diff line change
@@ -1,80 +1,114 @@
import traceback
import warnings
from django.template import Template, Context
from django.db import connection
import logging

import traceback
logger = logging.getLogger(__name__)

def _validate_sql(sqltext):
"""Validate the SQL query."""
if len(sqltext) < 8 or ("select" not in sqltext.lower()):
return False
return True

def _execute_query(cursor, sqltext):
"""Execute the SQL query and fetch data."""
cursor.execute(sqltext)
desc = cursor.description
result_as_list = [
dict(zip([col[0] for col in desc], row)) for row in cursor.fetchall()
]
columns = [col[0] for col in desc]
return columns, result_as_list

def _calculate_sums(data, columns, footerCols, totalText):
"""Calculate sum for specified columns."""
sumOfColumn = {col: 0 if col in footerCols else "-" for col in columns}

for d in data:
for attr, value in d.items():
if attr in footerCols:
sumOfColumn[attr] += float(str(value).replace(",", ""))

totalColumnSet = False
for col in sumOfColumn:
if sumOfColumn[col] != "-":
sumOfColumn[col] = format(float(str(sumOfColumn[col])), ",")
elif not totalColumnSet:
sumOfColumn[col] = totalText
totalColumnSet = True

return sumOfColumn

def _render_template(context):
"""Render the HTML table template."""
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 %} <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 %} <td align=\"center\">{{ forloop.counter }}</td> {% endif %} {% for attr, value in d.items %} <td align=\"center\">{{ value }}</td> {% endfor %} </tr> {% endfor %} "
" {% 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>"
)

return Template(template).render(Context(context))

def _generate_report(cursor, title, sqltext, footerCols, htmlClass, direction, font, totalText, rowIndex, headerRowColor, evenRowColor, oddRowColor):
"""Internal method to generate the SQL report."""
columns, data = _execute_query(cursor, sqltext)

sumOfColumn = None
if footerCols:
sumOfColumn = _calculate_sums(data, columns, footerCols, totalText)

context = {
'title': title,
'data': data,
'columns': columns,
'sumOfColumn': sumOfColumn,
'direction': direction,
'font': font,
'totalText': totalText,
'rowIndex': rowIndex,
'headerRowColor': headerRowColor,
'evenRowColor': evenRowColor,
'oddRowColor': oddRowColor,
'htmlClass': htmlClass
}

return _render_template(context)

def generate_from_sql(title, sqltext, footerCols=None, htmlClass="", direction="ltr", font="Tahoma", totalText="Total", rowIndex=False, headerRowColor='#eeeeee', evenRowColor='#ffffff', oddRowColor="#ffffff"):
"""Generate the SQL report with an internally created cursor."""
try:
if not _validate_sql(sqltext):
return 'Not Valid SQL'

with connection.cursor() as cursor:
return _generate_report(cursor, title, sqltext, footerCols, htmlClass, direction, font, totalText, rowIndex, headerRowColor, evenRowColor, oddRowColor)
except Exception as e:
logger.error(traceback.format_exc())
return f"Error: {str(e)}"

def generateFromSql(cursor, title, sqltext, footerCols=None, htmlClass="", direction="ltr", font="Tahoma", totalText="Total", rowIndex=False, headerRowColor='#eeeeee', evenRowColor='#ffffff', oddRowColor="#ffffff"):
"""Generate the SQL report with a provided cursor."""
warnings.warn("generateFromSql is deprecated and will be removed in a future release. Use generate_from_sql instead.", DeprecationWarning)
try:
if not _validate_sql(sqltext):
return 'Not Valid SQL'

def generateFromSql(cursor, title, sqltext, footerCols= None, htmlClass="", direction="ltr", font="Tahoma", totalText = "Total", rowIndex = False, headerRowColor ='#eeeeee' ,evenRowColor = '#ffffff', oddRowColor="#ffffff") :
sumCols=[]
try :
if(len(sqltext) < 8 or ("select" not in sqltext.lower())) :
return ('Not Valid SQL')

sql_query = sqltext
sumCols=footerCols
# execute sql query and retrieve data from db
cursor.execute(sql_query)

# retrieve columns of the data
desc = cursor.description

result_as_list = [
dict(zip([col[0] for col in desc ], row)) for row in cursor.fetchall()
]

columns = [col[0] for col in desc ] #result.keys()
data = result_as_list





sumOfColumn = None
if(sumCols != None) :
sumOfColumn={}

# initiating footer aggrigation values
for c in columns :
if(c in sumCols):
sumOfColumn[c]=0
else:
sumOfColumn[c]="-"

# travers in rows and aggrigate the values of columns those are in footerCols
for d in data :
for attr, value in dict(d).items() :
if(attr in sumCols):
sumOfColumn[attr]=sumOfColumn[attr]+float(str(value).replace(",", ""))

totalColumnSet = False
if(sumCols != None) :
for col, val in sumOfColumn.items() :
if(val!="-") :
sumOfColumn[col] = format(float(str(val)),",")
elif (totalColumnSet == False) :
sumOfColumn[col] = totalText
totalColumnSet = True

# template to generate data from data retrieved from database
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>"



c = Context({
'title':title,
'data':data,
'columns':columns,
'sumOfColumn':sumOfColumn,
'direction':direction,
'font':font,
'totalText':totalText,
'rowIndex' : rowIndex,
'headerRowColor' :headerRowColor ,
'evenRowColor' : evenRowColor,
'oddRowColor': oddRowColor,
'htmlClass' : htmlClass
})

return Template(template).render(c)
except BaseException as e :
#print exception trace to console
print(traceback.format_exc())
return ("Error :" + str(e))
return _generate_report(cursor, title, sqltext, footerCols, htmlClass, direction, font, totalText, rowIndex, headerRowColor, evenRowColor, oddRowColor)
except Exception as e:
logger.error(traceback.format_exc())
return f"Error: {str(e)}"

def generate_from_queryset(title, queryset, footerCols=None, htmlClass="", direction="ltr", font="Tahoma", totalText="Total", rowIndex=False, headerRowColor='#eeeeee', evenRowColor='#ffffff', oddRowColor="#ffffff"):
"""Generate the SQL report using a Django QuerySet."""
try:
sqltext = str(queryset.query)
return generate_from_sql(title, sqltext, footerCols, htmlClass, direction, font, totalText, rowIndex, headerRowColor, evenRowColor, oddRowColor)
except Exception as e:
logger.error(traceback.format_exc())
return f"Error: {str(e)}"
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -6,8 +6,8 @@

setup_args = dict(
name='django-query-to-table',
version='0.1.9',
description='A simple to use Django package to turn your sql query into a beautiful reporting html table',
version='1.0.0',
description='A simple to use Django package to turn your queryset and sql query into a beautiful reporting html table',
long_description_content_type="text/markdown",
long_description=README,
license='GNU',