diff options
author | Palaparthy Adityachandra | 2020-08-15 11:43:46 +0530 |
---|---|---|
committer | GitHub | 2020-08-15 11:43:46 +0530 |
commit | f8972ccd73c598cf0b0f20f13b4b8cf2179719e3 (patch) | |
tree | 47ad199e95c7b11ee4752c23a4cc04155872b580 | |
parent | f4ce8c9e3f20d0d4c873bfb8b0dd4d83ac0b8a58 (diff) | |
parent | 7d750d0559d4ff027900e58609cce842f07795b9 (diff) | |
download | workshop_booking-f8972ccd73c598cf0b0f20f13b4b8cf2179719e3.tar.gz workshop_booking-f8972ccd73c598cf0b0f20f13b4b8cf2179719e3.tar.bz2 workshop_booking-f8972ccd73c598cf0b0f20f13b4b8cf2179719e3.zip |
Merge pull request #158 from FOSSEE/fix_stats
Fix statistics app
-rw-r--r-- | requirements.txt | 3 | ||||
-rw-r--r-- | statistics_app/forms.py | 49 | ||||
-rw-r--r-- | statistics_app/templates/statistics_app/paginator.html | 34 | ||||
-rw-r--r-- | statistics_app/templates/statistics_app/team_stats.html | 152 | ||||
-rw-r--r-- | statistics_app/templates/statistics_app/workshop_public_stats.html | 470 | ||||
-rw-r--r-- | statistics_app/urls.py | 12 | ||||
-rw-r--r-- | statistics_app/views.py | 638 | ||||
-rw-r--r-- | teams/models.py | 9 | ||||
-rw-r--r-- | workshop_app/admin.py | 45 | ||||
-rw-r--r-- | workshop_app/models.py | 36 | ||||
-rw-r--r-- | workshop_app/static/workshop_app/css/base.css | 3 | ||||
-rw-r--r-- | workshop_app/templates/workshop_app/base.html | 22 | ||||
-rw-r--r-- | workshop_portal/settings.py | 2 | ||||
-rw-r--r-- | workshop_portal/urls.py | 1 |
14 files changed, 446 insertions, 1030 deletions
diff --git a/requirements.txt b/requirements.txt index 622c429..cc9dfd7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ Django==3.0.7 coverage pyaml django-recurrence -python-decouple==3.3
\ No newline at end of file +python-decouple==3.3 +pandas diff --git a/statistics_app/forms.py b/statistics_app/forms.py new file mode 100644 index 0000000..16d79c8 --- /dev/null +++ b/statistics_app/forms.py @@ -0,0 +1,49 @@ +# Django imports +from django import forms + +# Local Imports +from workshop_app.models import states, WorkshopType + + +class FilterForm(forms.Form): + from_date = forms.DateField( + widget=forms.DateInput( + attrs={'type': 'date', 'class': 'form-control'} + ) + ) + to_date = forms.DateField( + widget=forms.DateInput( + attrs={'type': 'date', 'class': 'form-control'} + ) + ) + workshop_type = forms.ModelChoiceField( + queryset=WorkshopType.objects.all(), required=False, + widget=forms.Select(attrs={'class': 'form-control'}) + ) + state = forms.ChoiceField( + choices=states, required=False, + widget=forms.Select(attrs={'class': 'form-control'}) + ) + show_workshops = forms.BooleanField( + help_text="Show my workshops only", required=False, + ) + sort = forms.ChoiceField( + choices=(("date", "Oldest"), ("-date", "Latest")), + help_text="Sort by", + widget=forms.Select(attrs={'class': 'form-control'}) + ) + def __init__(self, *args, **kwargs): + start = kwargs.pop("start") if "start" in kwargs else None + end = kwargs.pop("end") if "end" in kwargs else None + selected_state = kwargs.pop("state") if "state" in kwargs else None + selected_type = kwargs.pop("type") if "type" in kwargs else None + show_workshops = (kwargs.pop("show_workshops") + if "show_workshops" in kwargs else None) + sort = kwargs.pop("sort") if "sort" in kwargs else None + super(FilterForm, self).__init__(*args, **kwargs) + self.fields["from_date"].initial = start + self.fields["to_date"].initial = end + self.fields["state"].initial = selected_state + self.fields["workshop_type"].initial = selected_type + self.fields["show_workshops"].initial = show_workshops + self.fields["sort"].initial = sort diff --git a/statistics_app/templates/statistics_app/paginator.html b/statistics_app/templates/statistics_app/paginator.html new file mode 100644 index 0000000..9f0840a --- /dev/null +++ b/statistics_app/templates/statistics_app/paginator.html @@ -0,0 +1,34 @@ +<ul class="pagination pagination"> + {% if objects.has_previous %} + <li class="page-item"> + <a class="page-link" href="?page=1{% if request.GET.from_date %}&from_date={{ request.GET.from_date }}{% endif %}{% if request.GET.to_date %}&to_date={{ request.GET.to_date }}{% endif %}{% if request.GET.state %}&state={{ request.GET.state }}{% endif %}{% if request.GET.workshop_type %}&workshop_type={{ request.GET.workshop_type }}{% endif %}{% if request.GET.show_workshops %}&show_workshops={{ request.GET.show_workshops }}{% endif %}{% if request.GET.sort %}&sort={{ request.GET.sort }}{% endif %} + " aria-label="Previous"> + <span aria-hidden="true"> + <i class="fa fa-angle-double-left"></i> + </span> + <span class="sr-only">begin</span> + </a> + </li> + {% endif %} + + {% for n in objects.paginator.page_range %} + {% if objects.number == n %} + <li class="page-item active"> + <span class="page-link">{{ n }}<span class="sr-only">(current)</span></span> + </li> + {% elif n > objects.number|add:'-5' and n < objects.number|add:'5' %} + <li class="page-item"><a class="page-link" href="?page={{ n }}{% if request.GET.from_date %}&from_date={{ request.GET.from_date }}{% endif %}{% if request.GET.to_date %}&to_date={{ request.GET.to_date }}{% endif %}{% if request.GET.state %}&state={{ request.GET.state }}{% endif %}{% if request.GET.workshop_type %}&workshop_type={{ request.GET.workshop_type }}{% endif %}{% if request.GET.show_workshops %}&show_workshops={{ request.GET.show_workshops }}{% endif %}{% if request.GET.sort %}&sort={{ request.GET.sort }}{% endif %}">{{ n }}</a></li> + {% endif %} + {% endfor %} + + {% if objects.has_next %} + <li class="page-item"> + <a class="page-link" href="?page={{ objects.paginator.num_pages }}{% if request.GET.from_date %}&from_date={{ request.GET.from_date }}{% endif %}{% if request.GET.to_date %}&to_date={{ request.GET.to_date }}{% endif %}{% if request.GET.state %}&state={{ request.GET.state }}{% endif %}{% if request.GET.workshop_type %}&workshop_type={{ request.GET.workshop_type }}{% endif %}{% if request.GET.show_workshops %}&show_workshops={{ request.GET.show_workshops }}{% endif %}{% if request.GET.sort %}&sort={{ request.GET.sort }}{% endif %}" aria-label="Next"> + <span aria-hidden="true"> + <i class="fa fa-angle-double-right"></i> + </span> + <span class="sr-only">end</span> + </a> + </li> + {% endif %} +</ul>
\ No newline at end of file diff --git a/statistics_app/templates/statistics_app/team_stats.html b/statistics_app/templates/statistics_app/team_stats.html index 4e3e870..0342f61 100644 --- a/statistics_app/templates/statistics_app/team_stats.html +++ b/statistics_app/templates/statistics_app/team_stats.html @@ -4,121 +4,45 @@ Workshop Statistics {% endblock %} - -{% block extra %} - - <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"> - <script src="https://code.jquery.com/jquery-1.12.4.js"></script> - <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script> - - <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> - <script src="{{URL_ROOT}}/static/workshop_app/js/bootstrap-3.3.7.min.js"></script> - <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> - <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> - <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script> - <!-- For Charts --> - - <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.bundle.min.js"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script> - - <!-- For Google GeoChart India Map --> - <script type='text/javascript' src='https://www.google.com/jsapi'></script> - -{% endblock %} - {% block content %} -<div class="container"> -<div class="row"> - <br> - <div class="col-md-12 "> - <br> - <canvas id="myChartPie" ></canvas> - <script> - var dynamic_pie_color = []; - var data = {{ workshop_data.data}}; - - var dynamicColors = function() { - var r = Math.floor(Math.random() * 255); - var g = Math.floor(Math.random() * 255); - var b = Math.floor(Math.random() * 255); - return "rgb(" + r + "," + g + "," + b + ")"; - }; - - - for (var i in data) { - dynamic_pie_color.push(dynamicColors()); - } - - var data = { - datasets: [{ - data: data, - label: 'My dataset', // for legend - backgroundColor: dynamic_pie_color, - }], - labels: [{% for label in workshop_data.labels %}"{{ label }}",{% endfor %}] - }; - - var pieOptions = { - events: false, - animation: { - duration: 500, - easing: "easeOutQuart", - onComplete: function () { - var ctx = this.chart.ctx; - ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontFamily, 'normal', Chart.defaults.global.defaultFontFamily); - ctx.textAlign = 'center'; - ctx.textBaseline = 'bottom'; - - this.data.datasets.forEach(function (dataset) { - - for (var i = 0; i < dataset.data.length; i++) { - var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model, - total = dataset._meta[Object.keys(dataset._meta)[0]].total, - mid_radius = model.innerRadius + (model.outerRadius - model.innerRadius)/2, - start_angle = model.startAngle, - end_angle = model.endAngle, - mid_angle = start_angle + (end_angle - start_angle)/2; - - var x = mid_radius * Math.cos(mid_angle); - var y = mid_radius * Math.sin(mid_angle); - - ctx.fillStyle = '#fff'; - if (i == 3){ // Darker text color for lighter background - ctx.fillStyle = '#444'; - } - - var val = dataset.data[i]; - var percent = String(Math.round(val/total*100)) + "%"; - - if(val != 0) { - ctx.fillText(dataset.data[i], model.x + x, model.y + y); - // Display percent in another line, line break doesn't work for fillText - ctx.fillText(percent, model.x + x, model.y + y + 15); - } - } - }); - } - } - }; - - var pieChartCanvas = $("#myChartPie"); - var pieChart = new Chart(pieChartCanvas, { - type: 'pie', // or doughnut - data: data, - options: pieOptions - }); - </script> - <style> - #visualization path { - stroke-width:1; /* control the countries borders width */ - stroke:white; /* choose a color for the border */ - } - </style> - <div id="visualization" style="width: 400px; height: 300px; display: block; margin: 0 auto;" ></div> - - </div> -</div> +<div class="container-fluid"> + <div class="row"> + <div class="col-md-2"> + <ul class="nav nav-pills list-group"> + {% for team in all_teams %} + <li class="nav-item"> + <a href="{% url 'statistics_app:team' team.id %}" class="nav-link list-group-item"> + Team {{forloop.counter}} + </a> + </li> + {% endfor %} + </ul> + </div> + <div class="col-md-8"> + <br> + <canvas id="myChart"></canvas> + </div> + </div> </div> -<br> +<script> + var ctx = document.getElementById('myChart').getContext('2d'); + var chart = new Chart(ctx, { + // The type of chart we want to create + type: 'bar', + + // The data for our dataset + data: { + labels: {{team_labels|safe}}, + datasets: [{ + data: {{ws_count|safe}}, + label: "Team Members Workshops", + backgroundColor: 'rgb(255, 99, 132)', + borderColor: 'rgb(255, 99, 132)', + }] + }, + // Configuration options go here + options: {} + }); +</script> {% endblock %} diff --git a/statistics_app/templates/statistics_app/workshop_public_stats.html b/statistics_app/templates/statistics_app/workshop_public_stats.html index a42f818..ad55858 100644 --- a/statistics_app/templates/statistics_app/workshop_public_stats.html +++ b/statistics_app/templates/statistics_app/workshop_public_stats.html @@ -1,346 +1,140 @@ {% extends 'workshop_app/base.html' %} -{% block title %} - Open Statistics -{% endblock %} - -{% block extra %} - <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css"> - <script src="https://code.jquery.com/jquery-1.12.4.js"></script> - <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script> - - <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> - <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> - <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script> - - <!-- For Charts --> - <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.bundle.min.js"></script> - <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.6.0/Chart.min.js"></script> - - <!-- For Google GeoChart India Map --> - <script type='text/javascript' src='https://www.google.com/jsapi'></script> - -<script> - var dateToday = new Date(); - var upto = new Date(); - - dateToday.setDate(dateToday.getDate() - 1); - upto.setFullYear(dateToday.getFullYear() + 1); - - $( function() { - from = $( "#from" ) - .datepicker({ - defaultDate: "+1w", - changeMonth: true, - changeYear: true, - showButtonPanel: true, - maxDate: dateToday, - dateFormat: "yy-mm-dd" - }) - .on( "change", function() { - to.datepicker( "option", "minDate", getDate( this ) ); - }), - to = $( "#to" ).datepicker({ - defaultDate: "+1w", - changeMonth: true, - changeYear: true, - showButtonPanel: true, - minDate: dateToday, - maxDate: upto, - dateFormat: "yy-mm-dd" - }) - .on( "change", function() { - from.datepicker( "option", "maxDate", getDate( this ) ); - }); - - function getDate( element ) { - var date; - try { - date = $.datepicker.parseDate( dateFormat, element.value ); - } catch( error ) { - date = null; - } - return date; - } - } ); - - </script> - -{% endblock %} +{% block title %} Statistics {% endblock %} {% block content %} +<div class="container-fluid"> + <div class="row"> + <div class="col-md-3"> + <form method="GET"> + <div class="card"> + <div class="card-header"> + <div class="row"> + <div class="col-md-8"> + <h3><u>Filters</u></h3> + </div> + <div class="col-md-4"> + <a href="{% url 'statistics_app:public' %}" class="btn btn-outline-info"> + <i class="fa fa-times"></i> Clear + </a> + </div> + </div> + </div> + <div class="card-body"> + <div>From date: {{form.from_date}}</div> + <div>To date: {{form.to_date}}</div> + <div>Workshop: {{form.workshop_type}}</div> + <div>State: {{form.state}}</div> + <div>{{form.sort.help_text}}: {{form.sort}}</div> + {% if request.user.is_authenticated %} + <div>{{form.show_workshops.help_text}}: {{form.show_workshops}}</div> + {% endif %} + <br> + <div class="row justify-content-center"> + <div class="col-md-4"> + <button type="submit" class="btn btn-success"> + <i class="fa fa-eye"></i> View + </button> + </div> + <div class="col-md-6"> + <button type="submit" class="btn btn-info" name="download" value="download"> + <i class="fa fa-download"></i> Download + </button> + </div> + </div> + </div> + </div> + </form> + </div> + <div class="col"> + <div class="row"> + <div class="col-md-6"> + {% include "statistics_app/paginator.html" %} + </div> + <div class="col-md-3"> + <button class="btn btn-info" id="state_graph"> + <i class="fa fa-bar-chart"></i> State chart + </button> + </div> + <div class="col-md-3"> + <button class="btn btn-info" id="type_graph"> + <i class="fa fa-bar-chart"></i> Workshops chart + </button> + </div> + </div> + <table class="table table-responsive-sm"> + <thead> + <tr> + <th>Sr No.</th> + <th>Coordinator Name</th> + <th>Institute Name</th> + <th>Instructor Name</th> + <th>Workshop Name</th> + <th>Workshop Date</th> + </tr> + </thead> + {% csrf_token %} + {% for workshop in objects %} + <tbody> + <tr> + <td>{{ forloop.counter0|add:objects.start_index}}</td> + <td>{{ workshop.coordinator.get_full_name | capfirst }}</td> + <td>{{ workshop.coordinator.profile.institute | capfirst }}</td> + <td>{{ workshop.instructor.get_full_name | capfirst }}</td> + <td>{{ workshop.workshop_type.name }}</td> + <td>{{ workshop.date | date}}</td> + </tr> + </tbody> + {% endfor %} + </table> + {% include "statistics_app/paginator.html" %} + </div> + </div> +</div> -<div class="container"> - <div class="row"> - <div class="col-md-4" align="left" > - <fieldset data-mini="true"> - <label for="radio-2">Overall Count</label> - <input type="radio" name="radio-1" id="radio-2" value="OWC"> - <label for="radio-3">India Map</label> - <input type="radio" name="radio-1" id="radio-3" value="MOIN"> - </fieldset> - </div> - - <div class="col-md-8"> - <form method="POST" > - {% csrf_token %} - <div class="form-group"> - <label>state: </label> - <select name="states"> - <option value="all">ALL</option> - <option value="IN-AP">Andhra Pradesh</option> - <option value="IN-AR">Arunachal Pradesh</option - <option value="IN-AS">Assam</option> - <option value="IN-BR">Bihar</option> - <option value="IN-CT">Chhattisgarh</option> - <option value="IN-GA">Goa</option> - <option value="IN-GJ">Gujarat</option> - <option value="IN-HR">Haryana</option> - <option value="IN-HP">Himachal Pradesh</option> - <option value="IN-JK">Jammu and Kashmir</option> - <option value="IN-JH">Jharkhand</option> - <option value="IN-KA">Karnataka</option> - <option value="IN-KL">Kerala</option> - <option value="IN-MP">Madhya Pradesh</option> - <option value="IN-MH">Maharashtra</option> - <option value="IN-MN">Manipur</option> - <option value="IN-ML">Meghalaya</option> - <option value="IN-MZ">Mizoram</option> - <option value="IN-NL">Nagaland</option> - <option value="IN-OR">Odisha</option> - <option value="IN-PB">Punjab</option> - <option value="IN-RJ">Rajasthan</option> - <option value="IN-SK">Sikkim</option> - <option value="IN-TN">Tamil Nadu</option> - <option value="IN-TG">Telangana</option> - <option value="IN-TR">Tripura</option> - <option value="IN-UT">Uttarakhand</option> - <option value="IN-UP">Uttar Pradesh</option> - <option value="IN-WB">West Bengal</option> - <option value="IN-AN">Andaman and Nicobar Islands</option> - <option value="IN-CH">Chandigarh</option> - <option value="IN-DN">Dadra and Nagar Haveli</option> - <option value="IN-DD">Daman and Diu</option> - <option value="IN-DL">Delhi</option> - <option value="IN-LD">Lakshadweep</option> - <option value="IN-PY">Puducherry</option> - </select> - <label>WorkshopType: </label> - <select name="workshoptype_name"> - {% for workshop in workshoptype_list %} - <option value={{ workshop.id }}> {{ workshop }} </option> - {% endfor %} - </select> - - <br> - <label for="from">From</label> - <input type="text" id="from" name="from"> - <label for="to">to</label> - <input type="text" id="to" name="to"> - <button class="btn btn-info btn-sm" type="submit" name="View" value="View">View</button> - </div> - </form> - - {% if messages %} - <ul class="messages"> - {% for message in messages %} - <div class="alert alert-{{ message.tags }}"> - <li {% if message.tags %} class="{{ message.tags }}"{% endif %}> {{ message }} - </li> - </div> - {% endfor %} - </ul> - {% endif %} - - </div> - <br> - <table class="table table-hover"> - <thead> - <tr> - <th>Coordinator Name</th> - <th>Institute Name</th> - <th>Instructor Name</th> - <th>Workshop Name</th> - <th>Workshop Date</th> - <th>Initiated By</th> - </tr> - </thead> - {% csrf_token %} - {% for workshop in workshop_list %} - {% if workshop.proposed_workshop_date %} - <tbody> - <tr> - <td>{{ workshop.proposed_workshop_coordinator.get_full_name | capfirst }}</td> - <td>{{ workshop.proposed_workshop_coordinator.profile.institute | capfirst }}</td> - <td>{{ workshop.proposed_workshop_instructor.get_full_name }}</td> - <td>{{ workshop.proposed_workshop_title.workshoptype_name }}</td> - <td>{{ workshop.proposed_workshop_date | date}}</td> - <td>Coordinator</td> - </tr> - </tbody> - {% else %} - <tbody> - <tr> - <td>{{ workshop.requested_workshop_coordinator.get_full_name | capfirst }}</td> - <td>{{ workshop.requested_workshop_coordinator.profile.institute | capfirst }}</td> - <td>{{ workshop.requested_workshop_instructor.get_full_name }}</td> - <td>{{ workshop.requested_workshop_title.workshoptype_name }}</td> - <td>{{ workshop.requested_workshop_date | date}}</td> - <td>Instructor</td> - </tr> - </tbody> - {% endif %} - {% endfor %} - </table> - - <!-- Page Navigation --> - <div class="container"> - <div class="Page-Nav" align="center"> - <nav aria-label="Page navigation"> - <ul class="pagination pagination-sm"> - <li class="page-item"> - {% if workshop_list.has_previous %} - <a class="page-link" tabindex="-1" - href="?page={{ workshop_list.previous_page_number }}">Previous</a> - {% endif %} - </li> - <li class="page-item"> - <span class="current"> - Page {{ workshop_list.number }} of {{ workshop_list.paginator.num_pages }} - </span> - </li> - <li class="page-item"> - {% if workshop_list.has_next %} - <a class="page-link" href="?page={{ workshop_list.next_page_number }}">Next - </a> - {% endif %} - </li> - </ul> - </nav> - </div> - </div> - </div> - -<div class="row"> - <br> - <div class="col-md-12 "> - <br> - <canvas id="myChartPie" ></canvas> - <script> - $( function() { - $( "fieldset" ).controlgroup(); - }); - - var ctx1 = document.getElementById("myChartPie").getContext('2d'); - var myChart; - - $('input[type=radio]').change(function() { - if (this.value == 'OWC') { //Overall Workshop Count - if(myChart){ - myChart.destroy(); - } - - var dWidth = $(window).width() * 0.9; - var dHeight = $(window).height() * 0.9; - - $( "#myChartPie" ).dialog({ - resizable: false, - draggable: true, - title: 'Pie Chart', - closeOnEscape: true, - stack: true, - zIndex: 10000, - width: dWidth, - height: dHeight, - modal: true - }); - - document.getElementById("visualization").style.visibility='hidden'; - myChart = new Chart(ctx1, { - type: 'pie', - data: { - labels : {{ workshoptype_count.0 | safe}}, - datasets: [{ - data: {{ workshoptype_count.1 }}, - //Add more color scheme if workshop number increases - backgroundColor: [ - 'rgba(153, 102, 255, 1)', - 'rgba(191, 191, 1, 1)', - 'rgba(75, 192, 192, 1)', - 'rgba(153, 102, 255, 1)' - ] - }] - }, - options: { - responsive: true - } - }); - } - - else if (this.value == 'MOIN') { - if(myChart){ - myChart.destroy(); - } - - var dWidth = $(window).width() * 0.9; - var dHeight = $(window).height() * 0.9; - - $( "#visualization" ).dialog({ - resizable: false, - draggable: true, - title: 'State wise(Map of India)', - closeOnEscape: true, - stack: true, - zIndex: 10000, - width: dWidth, - height: dHeight, - modal: true - }); - - document.getElementById("visualization").style.visibility='visible'; - function drawVisualization() { - var data = google.visualization.arrayToDataTable( - {{ india_map | safe }} - ); - - var opts = { - region: 'IN', - domain: 'IN', - displayMode: 'regions', - resolution: 'provinces', - datalessRegionColor: 'transparent', - colorAxis: {colors: ['#e6e6e6','#ff8b3e']}, - - - }; +<!-- The Modal --> +<div id="dialog" title="Workshops Statistics"> + <canvas id="myChart"></canvas> +</div> - var geochart = new google.visualization.GeoChart( - document.getElementById('visualization')); - geochart.draw(data, opts); - }; - if(google) { - google.load('visualization', '1.0', { - packages: ['geochart'], - callback: function() { - // do stuff, if you wan't - it doesn't matter, because the page isn't blank! - drawVisualization(); - } - }) - } - } - }); - </script> - <style> - #visualization path { - stroke-width:1; /* control the countries borders width */ - stroke:white; - /* choose a color for the border */ - } - </style> - <div id="visualization" style="width: 400px; height: 300px; display: block; margin: 0 auto;" ></div> +<script type="text/javascript"> + var state_labels = {{ws_states|safe}}; + var state_data = {{ws_count|safe}}; + var type_labels = {{ws_type|safe}}; + var type_data = {{ws_type_count|safe}}; + var chart; + $("#state_graph").on("click", function() { + show_graph(state_labels, state_data, "State wise workshops"); + }); + $("#type_graph").on("click", function() { + show_graph(type_labels, type_data, "Type wise workshops"); + }); + + function show_graph(labels, data, graph_name, chart_name) { + var ctx = document.getElementById('myChart').getContext('2d'); + if(chart) chart.destroy(); + chart = new Chart(ctx, { + // The type of chart we want to create + type: 'bar', + + // The data for our dataset + data: { + labels: labels, + datasets: [{ + data: data, + label: graph_name, + backgroundColor: 'rgb(255, 99, 132)', + borderColor: 'rgb(255, 99, 132)', + }] + }, + // Configuration options go here + options: {} + }); + $("#dialog").dialog({ + width: 900, + height: 500 + }); + } +</script> - </div> -</div> -</div> -<br> {% endblock %} diff --git a/statistics_app/urls.py b/statistics_app/urls.py index 8950e37..05d7ac9 100644 --- a/statistics_app/urls.py +++ b/statistics_app/urls.py @@ -1,12 +1,10 @@ - -from django.conf.urls import url -from django.contrib import admin +from django.urls import path from statistics_app import views +app_name = "statistics_app" urlpatterns = [ - url(r'^statistics/$', views.workshop_stats), - url(r'^statistics/public_stats/$', views.workshop_public_stats), - url(r'^statistics/profile_stats/$', views.profile_stats), - url(r'^statistics/v1/team_stats/$', views.team_stats), + path('public', views.workshop_public_stats, name="public"), + path('team', views.team_stats, name="team"), + path('team/<int:team_id>', views.team_stats, name="team"), ] diff --git a/statistics_app/views.py b/statistics_app/views.py index 784f30d..3c9fcae 100644 --- a/statistics_app/views.py +++ b/statistics_app/views.py @@ -1,569 +1,129 @@ -from workshop_app.forms import ( - UserRegistrationForm, UserLoginForm, - ProfileForm, CreateWorkshop, - ProposeWorkshopDateForm -) -from workshop_app.models import ( - Profile, User, - has_profile, Workshop, - WorkshopType, RequestedWorkshop, - BookedWorkshop, ProposeWorkshopDate, - Testimonial -) -from teams.models import Team +# Python Imports +import datetime as dt +import pandas as pd +# Django Imports from django.template.loader import get_template from django.template import RequestContext -from datetime import datetime, date -from django.contrib.auth import login, logout, authenticate +from django.contrib import messages from django.contrib.auth.decorators import login_required -from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger +from django.core.paginator import Paginator from django.shortcuts import render, redirect -from django.db import IntegrityError +from django.urls import reverse from django.utils import timezone -from django.http import HttpResponse, HttpResponseRedirect -from django.conf import settings -from os import listdir, path, sep -from zipfile import ZipFile -from django.contrib import messages -from operator import itemgetter -import datetime as dt -import csv -try: - from StringIO import StringIO as string_io -except ImportError: - from io import BytesIO as string_io - +from django.http import HttpResponse -# Create your views here. -def check_workshop_type(x): - try: - y = datetime.strftime(x.proposed_workshop_date, '%d-%m-%Y') - except BaseException: - y = datetime.strftime(x.requested_workshop_date, '%d-%m-%Y') - return y +# Local Imports +from workshop_app.models import ( + Profile, User, has_profile, Workshop, WorkshopType, Testimonial, + states +) +from teams.models import Team +from .forms import FilterForm def is_instructor(user): '''Check if the user is having instructor rights''' - return True if user.groups.filter(name='instructor').count() > 0 else False + return user.groups.filter(name='instructor').exists() def is_email_checked(user): if hasattr(user, 'profile'): - return True if user.profile.is_email_verified else False + return user.profile.is_email_verified else: return False -def pie_chart(): - '''This function gives Data for drawring Pie Chart''' - - # Count Total Number of workshops for each type - workshop_titles = WorkshopType.objects.all() - workshoptype_dict = {} - for title in workshop_titles: - workshoptype_dict[title] = 0 - - for title in workshoptype_dict.keys(): - workshoptype_dict[title] += RequestedWorkshop.objects.filter( - requested_workshop_title=title, - status='ACCEPTED').count() - workshoptype_dict[title] += ProposeWorkshopDate.objects.filter( - proposed_workshop_title=title, - status='ACCEPTED').count() - - # For Pie Chart - workshoptype_num = [count for count in workshoptype_dict.values()] - workshoptype_title = [str(title) for title in workshoptype_dict.keys()] - - workshoptype_count = [workshoptype_title, workshoptype_num] - del workshoptype_title, workshoptype_num - - return workshoptype_count - - -def india_map(): - '''This function returns count of workshops based on states in India.''' - - states = [ - ['Code', 'State', 'Number'], - ["IN-AP", "Andhra Pradesh", 0], - ["IN-AR", "Arunachal Pradesh", 0], - ["IN-AS", "Assam", 0], - ["IN-BR", "Bihar", 0], - ["IN-CT", "Chhattisgarh", 0], - ["IN-GA", "Goa", 0], - ["IN-GJ", "Gujarat", 0], - ["IN-HR", "Haryana", 0], - ["IN-HP", "Himachal Pradesh", 0], - ["IN-JK", "Jammu and Kashmir", 0], - ["IN-JH", "Jharkhand", 0], - ["IN-KA", "Karnataka", 0], - ["IN-KL", "Kerala", 0], - ["IN-MP", "Madhya Pradesh", 0], - ["IN-MH", "Maharashtra", 0], - ["IN-MN", "Manipur", 0], - ["IN-ML", "Meghalaya", 0], - ["IN-MZ", "Mizoram", 0], - ["IN-NL", "Nagaland", 0], - ["IN-OR", "Odisha", 0], - ["IN-PB", "Punjab", 0], - ["IN-RJ", "Rajasthan", 0], - ["IN-SK", "Sikkim", 0], - ["IN-TN", "Tamil Nadu", 0], - ["IN-TG", "Telangana", 0], - ["IN-TR", "Tripura", 0], - ["IN-UT", "Uttarakhand", 0], - ["IN-UP", "Uttar Pradesh", 0], - ["IN-WB", "West Bengal", 0], - ["IN-AN", "Andaman and Nicobar Islands", 0], - ["IN-CH", "Chandigarh", 0], - ["IN-DN", "Dadra and Nagar Haveli", 0], - ["IN-DD", "Daman and Diu", 0], - ["IN-DL", "Delhi", 0], - ["IN-LD", "Lakshadweep", 0], - ["IN-PY", "Puducherry", 0] - ] - workshop_state = [] - requestedWorkshops = RequestedWorkshop.objects.filter(status='ACCEPTED') - proposedWorkshops = ProposeWorkshopDate.objects.filter(status='ACCEPTED') - for workshop in requestedWorkshops: - for s in states: - if s[0] == workshop.requested_workshop_coordinator.profile.state: - s[2] += 1 - - for workshop in proposedWorkshops: - for s in states: - if s[0] == workshop.proposed_workshop_coordinator.profile.state: - s[2] += 1 - - return states - - - -def monthly_accepted_chart(): - '''This function returns workshops accepted for each month - in the current year''' - today = datetime.now() - upto = today + dt.timedelta(days=15) - - workshop_count = [0] * 12 - for x in range(12): - workshop_count[x] += RequestedWorkshop.objects.filter( - requested_workshop_date__year=str(today.year), - requested_workshop_date__month=str(x + 1), - status='ACCEPTED').count() - workshop_count[x] += ProposeWorkshopDate.objects.filter( - proposed_workshop_date__year=str(today.year), - proposed_workshop_date__month=str(x + 1), - status='ACCEPTED').count() - - return workshop_count - - -@login_required -def workshop_stats(request): - user = request.user - today = datetime.now() - upto = today + dt.timedelta(days=15) - - #For Monthly Chart - workshop_count = monthly_accepted_chart() - - # For Pie Chart - workshoptype_count = pie_chart() - - # For India Map - states = india_map() - # For Data Downloading and Viewing - if request.method == 'POST': - try: - from_dates = request.POST.get('from') - to_dates = request.POST.get('to') - - # Fetches Accepted workshops which were proposed by Coordinators - proposed_workshops = ProposeWorkshopDate.objects.filter( - proposed_workshop_date__range=(from_dates, to_dates), - status='ACCEPTED' - ) - - # Fetches Accepted workshops which were Accepted by - # Instructors based on their Availability - requested_workshops = RequestedWorkshop.objects.filter( - requested_workshop_date__range=(from_dates, to_dates), - status='ACCEPTED' - ) - - upcoming_workshops = [] - - for workshop in proposed_workshops: - upcoming_workshops.append(workshop) - - for workshop in requested_workshops: - upcoming_workshops.append(workshop) - - upcoming_workshops = sorted(upcoming_workshops, - key=lambda x: check_workshop_type(x)) - - download = request.POST.get('Download') - if download: - response = HttpResponse(content_type='text/csv') - - response['Content-Disposition'] = 'attachment;\ - filename="records_from_{0}_to_{1}.csv"'.format( - from_dates, to_dates - ) - - writer = csv.writer(response) - header = [ - 'coordinator name', - 'instructor name', - 'workshop', - 'date', - 'status', - 'institute name', - 'state' - ] - - writer.writerow(header) - - for workshop in upcoming_workshops: - try: - row = [ - workshop.proposed_workshop_coordinator, - workshop.proposed_workshop_instructor, - workshop.proposed_workshop_title, - workshop.proposed_workshop_date, - workshop.status, - workshop.proposed_workshop_coordinator.profile.institute, - str(workshop.proposed_workshop_coordinator.profile.state) - ] - - except BaseException: - row = [ - workshop.requested_workshop_coordinator, - workshop.requested_workshop_instructor, - workshop.requested_workshop_title, - workshop.requested_workshop_date, - workshop.status, - workshop.requested_workshop_coordinator.profile.institute, - str(workshop.requested_workshop_coordinator.profile.state) - ] - - writer.writerow(row) - return response - else: - return render(request, - 'statistics_app/workshop_stats.html', - {"upcoming_workshops": upcoming_workshops, - "show_workshop_stats": settings.SHOW_WORKSHOP_STATS, - "workshop_count": workshop_count, - "workshoptype_count": workshoptype_count, - "india_map": states}) - except BaseException: - messages.info(request, 'Please enter Valid Dates') - - if is_instructor(user) and is_email_checked(user): - try: - # Fetches Accepted workshops which were proposed by Coordinators - proposed_workshops = ProposeWorkshopDate.objects.filter( - proposed_workshop_date__range=(today, upto), - status='ACCEPTED' - ) - - # Fetches Accepted workshops which were Accepted by - # Instructors based on their Availability - requested_workshops = RequestedWorkshop.objects.filter( - requested_workshop_date__range=(today, upto), - status='ACCEPTED' - ) - - upcoming_workshops = [] - for workshop in proposed_workshops: - upcoming_workshops.append(workshop) - - for workshop in requested_workshops: - upcoming_workshops.append(workshop) - - upcoming_workshops = sorted(upcoming_workshops, - key=lambda x: check_workshop_type(x)) - - except BaseException: - upcoming_workshops = [] - - paginator = Paginator(upcoming_workshops, 12) - - page = request.GET.get('page') - try: - upcoming_workshops = paginator.page(page) - except PageNotAnInteger: - # If page is not an integer, deliver first page. - upcoming_workshops = paginator.page(1) - except EmptyPage: - # If page is out of range(e.g 999999), deliver last page. - upcoming_workshops = paginator.page(paginator.num_pages) - - return render(request, 'statistics_app/workshop_stats.html', - { - "upcoming_workshops": upcoming_workshops, - "show_workshop_stats": settings.SHOW_WORKSHOP_STATS, - "workshop_count": workshop_count, - "workshoptype_count": workshoptype_count, - "india_map": states - }) - else: - return redirect('/statistics/public_stats/') - - def workshop_public_stats(request): user = request.user - today = datetime.now() - upto = today + dt.timedelta(days=15) - - #For Pie Chart - workshoptype_count = pie_chart() - - # For India Map - states = india_map() - - # Select By WorkshopType - workshoptype_list = list(WorkshopType.objects.all()) - workshoptype_list.append('all') - - # For Data Viewing - if request.method == 'POST': - workshop_list = [] - - try: - from_dates = request.POST.get('from') - to_dates = request.POST.get('to') - selected_state = request.POST.get('states') - selected_workshoptype = request.POST.get('workshoptype_name') - if selected_state == 'all': - # If dates not passed - if len(from_dates) == 0 and len(to_dates) == 0: - from_dates = '2017-01-01' - to_dates = upto - # If SelectedWorkshopType is 'All' - if len(selected_workshoptype) == 0: - # Fetches Accepted workshops which were proposed by - # Coordinators - proposed_workshops = ProposeWorkshopDate.objects.filter( - proposed_workshop_date__range=(from_dates, to_dates), - status='ACCEPTED' - ) - - # Fetches Accepted workshops which were Accepted by - # Instructors based on their Availability - requested_workshops = RequestedWorkshop.objects.filter( - requested_workshop_date__range=(from_dates, to_dates), - status='ACCEPTED' - ) - - else: - # Fetches Accepted workshops which were proposed by - # Coordinators - proposed_workshops = ProposeWorkshopDate.objects.filter( - proposed_workshop_date__range=(from_dates, to_dates), - status='ACCEPTED', proposed_workshop_title_id=int(selected_workshoptype) - ) - - # Fetches Accepted workshops which were Accepted by - # Instructors based on their Availability - requested_workshops = RequestedWorkshop.objects.filter( - requested_workshop_date__range=(from_dates, to_dates), - status='ACCEPTED', requested_workshop_title_id=int(selected_workshoptype) - ) - - for workshop in proposed_workshops: - workshop_list.append(workshop) - - for workshop in requested_workshops: - workshop_list.append(workshop) - - else: - # If dates not passed - if len(from_dates) == 0 and len(to_dates) == 0: - from_dates = '2017-01-01' - to_dates = upto - - # Get list of coordinators - coordinators_state_wise = Profile.objects.filter( - state=selected_state) - - if len(selected_workshoptype) == 0: - # Traverse through list of coordinators and append - # workshop_list - for coordinator in coordinators_state_wise: - # Fetches Accepted workshops which were proposed by - # Coordinators - proposed_workshops = ProposeWorkshopDate.objects.filter( - proposed_workshop_date__range=( - from_dates, - to_dates), - status='ACCEPTED', - proposed_workshop_coordinator=coordinator.user_id) - - # Fetches Accepted workshops which were Accepted by - # Instructors based on their Availability - requested_workshops = RequestedWorkshop.objects.filter( - requested_workshop_date__range=( - from_dates, - to_dates), - status='ACCEPTED', - requested_workshop_coordinator=coordinator.user_id) - - for workshop in proposed_workshops: - workshop_list.append(workshop) - - for workshop in requested_workshops: - workshop_list.append(workshop) - - else: - # Traverse through list of coordinators and append - # workshop_list - for coordinator in coordinators_state_wise: - # Fetches Accepted workshops which were proposed by - # Coordinators - proposed_workshops = ProposeWorkshopDate.objects.filter( - proposed_workshop_date__range=( - from_dates, - to_dates), - status='ACCEPTED', proposed_workshop_title_id=int(selected_workshoptype), - proposed_workshop_coordinator=coordinator.user_id) - - # Fetches Accepted workshops which were Accepted by - # Instructors based on their Availability - requested_workshops = RequestedWorkshop.objects.filter( - requested_workshop_date__range=( - from_dates, - to_dates), - status='ACCEPTED', requested_workshop_title_id=int(selected_workshoptype), - requested_workshop_coordinator=coordinator.user_id) - - for workshop in proposed_workshops: - workshop_list.append(workshop) - - for workshop in requested_workshops: - workshop_list.append(workshop) - - return render(request, - 'statistics_app/workshop_public_stats.html', - { - "workshoptype_list": workshoptype_list[::-1], - "workshop_list": workshop_list, - "workshoptype_count": workshoptype_count, - "india_map": states}) - - except BaseException: - messages.info(request, 'Please enter Valid Dates') - + form = FilterForm() + from_date = request.GET.get('from_date') + to_date = request.GET.get('to_date') + state = request.GET.get('state') + workshoptype = request.GET.get('workshop_type') + show_workshops = request.GET.get('show_workshops') + sort = request.GET.get('sort') + download = request.GET.get('download') + + if from_date and to_date: + form = FilterForm( + start=from_date, end=to_date, state=state, type=workshoptype, + show_workshops=show_workshops, sort=sort + ) + workshops = Workshop.objects.filter( + date__range=(from_date, to_date), status=1 + ).order_by(sort) + if state: + workshops = workshops.filter(coordinator__profile__state=state) + if workshoptype: + workshops = workshops.filter(workshop_type_id=workshoptype) else: - '''Default Data''' - workshop_list = [] - - proposed_workshops = ProposeWorkshopDate.objects.filter( - status='ACCEPTED') - - # Fetches Accepted workshops which were Accepted by - # Instructors based on their Availability - requested_workshops = RequestedWorkshop.objects.filter( - status='ACCEPTED') - - for workshop in proposed_workshops: - workshop_list.append(workshop) - - for workshop in requested_workshops: - workshop_list.append(workshop) - - - return render(request, - 'statistics_app/workshop_public_stats.html', - { - "workshoptype_list": workshoptype_list[::-1], - "workshoptype_count": workshoptype_count, - "workshop_list": workshop_list, - "india_map": states}) + today = timezone.now() + upto = today + dt.timedelta(days=15) + workshops = Workshop.objects.filter( + date__range=(today, upto), status=1 + ).order_by("date") + if show_workshops: + if is_instructor(user): + workshops = workshops.filter(instructor_id=user.id) + else: + workshops = workshops.filter(coordinator_id=user.id) + if download: + data = workshops.values( + "workshop_type__name", "coordinator__first_name", + "coordinator__last_name", "instructor__first_name", + "instructor__last_name", "coordinator__profile__state", + "date", "status" + ) + df = pd.DataFrame(data) + if not df.empty: + df.status.replace( + [0, 1, 2], ['Pending', 'Success', 'Reject'], inplace=True + ) + codes, states = list(zip(*states)) + df.coordinator__profile__state.replace(codes, states, inplace=True) + response = HttpResponse(content_type='text/csv') + response['Content-Disposition'] = f'attachment; filename=statistics.csv' + output_file = df.to_csv(response, index=False) + return response + else: + messages.add_message(request, messages.WARNING, "No data found") + ws_states, ws_count = Workshop.objects.get_workshops_by_state(workshops) + ws_type, ws_type_count = Workshop.objects.get_workshops_by_type(workshops) + paginator = Paginator(workshops, 30) + page = request.GET.get('page') + workshops = paginator.get_page(page) + context = {"form": form, "objects": workshops, "ws_states": ws_states, + "ws_count": ws_count, "ws_type": ws_type, + "ws_type_count": ws_type_count} + return render( + request, 'statistics_app/workshop_public_stats.html', context + ) @login_required -def profile_stats(request): +def team_stats(request, team_id=None): user = request.user - if is_instructor(user) and is_email_checked(user): - profiles = Profile.objects.all() - - rworkshops = RequestedWorkshop.objects.filter(status='ACCEPTED') - pworkshops = ProposeWorkshopDate.objects.filter(status='ACCEPTED') - - iprofile = Profile.objects.filter(position='instructor') - cprofile = Profile.objects.filter(position='coordinator') - - instructor_profile = [] - coordinator_profile = [] - - for p in iprofile: - instructor_profile.append({"profile": p, - "count": 0 - }) - - - for p in cprofile: - coordinator_profile.append({"profile": p, - "count": 0 - }) - - for p in instructor_profile: - p['count'] += RequestedWorkshop.objects.filter( - requested_workshop_instructor_id=p['profile'].user.id, - status='ACCEPTED').count() - - p['count'] += ProposeWorkshopDate.objects.filter( - proposed_workshop_instructor_id=p['profile'].user.id, - status='ACCEPTED').count() - - for p in coordinator_profile: - p['count'] += RequestedWorkshop.objects.filter( - requested_workshop_coordinator_id=p['profile'].user.id, - status='ACCEPTED').count() - - p['count'] += ProposeWorkshopDate.objects.filter( - proposed_workshop_coordinator_id=p['profile'].user.id, - status='ACCEPTED').count() - - #Sorting - coordinator_profile = sorted(coordinator_profile, - key=lambda k:k['count'], reverse=True) - instructor_profile = sorted(instructor_profile, - key=lambda k:k['count'], reverse=True) - - return render(request, "statistics_app/profile_stats.html", - { - "instructor_data": instructor_profile, - "coordinator_data": coordinator_profile, - }) + teams = Team.objects.all() + if team_id: + team = teams.get(id=team_id) else: - logout(request) - return render(request, "workshop_app/logout.html") - -@login_required -def team_stats(request, team_id): - user = request.user - team = Team.objects.get(team_id) - if user not in team.members.all(): - if user.groups.filter(name='instructor').count() > 0: - return redirect('/manage/') - return redirect('/book/') + team = teams.first() + if not team.members.filter(user_id=user.id).exists(): + messages.add_message( + request, messages.INFO, "You are not added to the team" + ) + return redirect(reverse("workshop_app:index")) member_workshop_data = {} for member in team.members.all(): - workshop_count = ProposeWorkshopDate.objects.filter(proposed_workshop_instructor=member.id).count() - if member_workshop_data.get('data'): - member_workshop_data['data'].append(workshop_count) - else: - member_workshop_data['data'] = [workshop_count] - if member_workshop_data.get('labels'): - member_workshop_data['labels'].append(member.user.username) - else: - member_workshop_data['labels'] = [member.user.username] - return render(request, 'statistics_app/team_stats.html', {'workshop_data': member_workshop_data}) + workshop_count = Workshop.objects.filter( + instructor_id=member.user.id).count() + member_workshop_data[member.user.get_full_name()] = workshop_count + team_labels = list(member_workshop_data.keys()) + ws_count = list(member_workshop_data.values()) + return render( + request, 'statistics_app/team_stats.html', + {'team_labels': team_labels, "ws_count": ws_count, 'all_teams': teams, + 'team_id': team.id} + ) diff --git a/teams/models.py b/teams/models.py index b0f6fe4..ed91f73 100644 --- a/teams/models.py +++ b/teams/models.py @@ -4,6 +4,9 @@ from django.contrib.auth.models import User from workshop_app.models import Profile class Team(models.Model): - members = models.ManyToManyField(Profile) - creator = models.OneToOneField(User, on_delete=models.CASCADE) - created_date = models.DateTimeField(auto_now=True)
\ No newline at end of file + members = models.ManyToManyField(Profile) + creator = models.OneToOneField(User, on_delete=models.CASCADE) + created_date = models.DateTimeField(auto_now=True) + + def __str__(self): + return f"Team created by {self.creator.get_full_name()}" diff --git a/workshop_app/admin.py b/workshop_app/admin.py index 988351f..d236c39 100644 --- a/workshop_app/admin.py +++ b/workshop_app/admin.py @@ -1,4 +1,4 @@ -import csv +import pandas as pd from django.contrib import admin from django.http import HttpResponse @@ -23,21 +23,15 @@ class ProfileAdmin(admin.ModelAdmin): actions = ['download_csv'] def download_csv(self, request, queryset): - openfile = string_io() + data = queryset.values( + "title", "user__first_name", "user__last_name", + "user__email", "institute", "location", "department", + "phone_number" + ) + df = pd.DataFrame(data) response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment;filename=profile_data.csv' - writer = csv.writer(response) - writer.writerow(['email_id', 'title', 'username', 'first_name', 'last_name', - 'institute', 'location', 'department', - 'phone_number', 'position']) - for q in queryset: - writer.writerow([q.user.email, q.title, q.user, q.user.first_name, - q.user.last_name, q.institute, - q.location, q.department, q.phone_number, - q.position]) - - openfile.seek(0) - response.write(openfile.read()) + response['Content-Disposition'] = f'attachment; filename=profile.csv' + output_file = df.to_csv(response, index=False) return response download_csv.short_description = "Download CSV file for selected stats." @@ -49,17 +43,18 @@ class WorkshopAdmin(admin.ModelAdmin): actions = ['download_csv'] def download_csv(self, request, queryset): - openfile = string_io() + data = queryset.values( + "workshop_type__name", "date", "coordinator__first_name", + "coordinator__last_name", "instructor__first_name", + "instructor__last_name", "status" + ) + df = pd.DataFrame(data) + df.status.replace( + [0, 1, 2], ['Pending', 'Success', 'Reject'], inplace=True + ) response = HttpResponse(content_type='text/csv') - response['Content-Disposition'] = 'attachment;filename=workshop_data.csv' - writer = csv.writer(response) - writer.writerow(['workshop_type', 'date', 'instructor', 'coordinator', 'status']) - - for q in queryset: - writer.writerow([q.title, q.date, q.instructor, q.coordinator, q.status]) - - openfile.seek(0) - response.write(openfile.read()) + response['Content-Disposition'] = f'attachment; filename=workshops.csv' + output_file = df.to_csv(response, index=False) return response download_csv.short_description = "Download CSV file for selected stats." diff --git a/workshop_app/models.py b/workshop_app/models.py index 62d10b5..48856fd 100644 --- a/workshop_app/models.py +++ b/workshop_app/models.py @@ -1,5 +1,6 @@ import os import uuid +import pandas as pd from django.contrib.auth.models import User from django.core.validators import RegexValidator @@ -44,6 +45,7 @@ source = ( ) states = ( + ("", "---------"), ("IN-AP", "Andhra Pradesh"), ("IN-AR", "Arunachal Pradesh"), ("IN-AS", "Assam"), @@ -85,7 +87,7 @@ states = ( def has_profile(user): """ check if user has profile """ - return True if hasattr(user, 'profile') else False + return hasattr(user, 'profile') def attachments(instance, filename): @@ -155,6 +157,34 @@ class AttachmentFile(models.Model): workshop_type = models.ForeignKey(WorkshopType, on_delete=models.CASCADE) +class WorkshopManager(models.Manager): + + def get_workshops_by_state(self, workshops): + w = workshops.values_list("coordinator__profile__state", flat=True) + states_map = dict(states) + df = pd.DataFrame(w) + data_states, data_counts = [], [] + if not df.empty: + grouped_data = df.value_counts().to_dict() + for state, count in grouped_data.items(): + state_name = state[0] + data_states.append(states_map[state_name]) + data_counts.append(count) + return data_states, data_counts + + def get_workshops_by_type(self, workshops): + w = workshops.values_list("workshop_type__name", flat=True) + df = pd.DataFrame(w) + data_wstypes, data_counts = [], [] + if not df.empty: + grouped_data = df.value_counts().to_dict() + for ws, count in grouped_data.items(): + ws_name = ws[0] + data_wstypes.append(ws_name) + data_counts.append(count) + return data_wstypes, data_counts + + class Workshop(models.Model): """ Contains details of workshops @@ -179,6 +209,8 @@ class Workshop(models.Model): help_text="I accept the terms and conditions" ) + objects = WorkshopManager() + def __str__(self): return f"{self.workshop_type} on {self.date} by {self.coordinator}" @@ -187,6 +219,8 @@ class Workshop(models.Model): return choice.get(self.status) + + class Testimonial(models.Model): """ Contains Testimonals of Workshops diff --git a/workshop_app/static/workshop_app/css/base.css b/workshop_app/static/workshop_app/css/base.css index 31f8da5..e8dd4ed 100644 --- a/workshop_app/static/workshop_app/css/base.css +++ b/workshop_app/static/workshop_app/css/base.css @@ -2,7 +2,7 @@ * Footer Styles */ .navbar { - z-index: 1000; + z-index: 1000; } .footer { position: fixed; @@ -11,6 +11,7 @@ background-color: #61615F; color: white; text-align: center; + z-index: 100; } .base-content { padding-top: 5em; diff --git a/workshop_app/templates/workshop_app/base.html b/workshop_app/templates/workshop_app/base.html index 7535f6a..ba968ba 100644 --- a/workshop_app/templates/workshop_app/base.html +++ b/workshop_app/templates/workshop_app/base.html @@ -14,6 +14,8 @@ <script src="{% static 'workshop_app/js/bootstrap.min.js' %}"></script> <script src="{% static 'workshop_app/js/popper.min.js' %}"></script> <script src="{% static 'workshop_app/js/toastr.min.js' %}"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script> {% block extra-dependencies %} @@ -24,6 +26,7 @@ <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link rel="stylesheet" href="{% static 'workshop_app/css/base.css' %}" type="text/css"/> <link rel="stylesheet" href="{% static 'workshop_app/css/font-awesome.css' %}" type="text/css"/> + <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css"> {% block extra-custom-scripts %} {% endblock %} @@ -43,8 +46,27 @@ <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> + <ul class="navbar-nav"> + <li class="nav-item"> + <a class="nav-link text-white" href="/"> + Home + </a> + </li> + <li class="nav-item"> + <a class="nav-link text-white" href="{% url 'statistics_app:public' %}"> + Workshop Statistics + </a> + </li> + </ul> {% if user.is_authenticated %} <ul class="navbar-nav mr-auto"> + {% if request.user|has_group:"instructor" %} + <li class="nav-item"> + <a class="nav-link text-white" href="{% url 'statistics_app:team' %}"> + Team Statistics + </a> + </li> + {% endif %} <li class="nav-item"> <a class="nav-link text-white" href="{% url 'workshop_app:workshop_status_instructor' %}"> Workshop Status diff --git a/workshop_portal/settings.py b/workshop_portal/settings.py index 78f0358..67f7f12 100644 --- a/workshop_portal/settings.py +++ b/workshop_portal/settings.py @@ -134,7 +134,7 @@ STATIC_URL = '/static/' STATIC_ROOT = 'workshop_app/static/' -LOGIN_URL = '/login/' +LOGIN_URL = '/workshop/login/' MEDIA_URL = '/data/' diff --git a/workshop_portal/urls.py b/workshop_portal/urls.py index 45f1407..59a7431 100644 --- a/workshop_portal/urls.py +++ b/workshop_portal/urls.py @@ -26,6 +26,7 @@ urlpatterns = [ url(r'^workshop/', include('workshop_app.urls')), url(r'^reset/', include('django.contrib.auth.urls')), url(r'^page/', include('cms.urls')), + url(r'^statistics/', include('statistics_app.urls')), ] urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) |