diff options
author | Akshen | 2017-09-05 14:05:25 +0530 |
---|---|---|
committer | Akshen | 2017-09-05 14:05:25 +0530 |
commit | 47cdf0a98730ba006a24d4d710afdad04c861363 (patch) | |
tree | cc56921a1199fd57d4f612e3efa279a07d335405 /workshop_app | |
parent | 86e0b947d7d8cf20cd354d6653b1c84c0e6d124f (diff) | |
download | workshop_booking-47cdf0a98730ba006a24d4d710afdad04c861363.tar.gz workshop_booking-47cdf0a98730ba006a24d4d710afdad04c861363.tar.bz2 workshop_booking-47cdf0a98730ba006a24d4d710afdad04c861363.zip |
Updates Workshop Stats
- Updates .gitignore
- Updates footer, sets it to fixed position
- Adds Workshop Stats
- Instructor can Download or View Workshop stats from the givin date range
- Instructor can view number of workshops per month in chart
- Writes test for Workshop Stats
Diffstat (limited to 'workshop_app')
-rw-r--r-- | workshop_app/forms.py | 4 | ||||
-rw-r--r-- | workshop_app/reminder_script.py | 10 | ||||
-rw-r--r-- | workshop_app/static/workshop_app/css/sticky-footer.css | 18 | ||||
-rw-r--r-- | workshop_app/templates/workshop_app/base.html | 2 | ||||
-rw-r--r-- | workshop_app/templates/workshop_app/edit_profile.html | 2 | ||||
-rw-r--r-- | workshop_app/templates/workshop_app/manage.html | 4 | ||||
-rw-r--r-- | workshop_app/templates/workshop_app/my_workshops.html | 14 | ||||
-rw-r--r-- | workshop_app/templates/workshop_app/profile_updated.html | 2 | ||||
-rw-r--r-- | workshop_app/templates/workshop_app/propose_workshop.html | 2 | ||||
-rw-r--r-- | workshop_app/templates/workshop_app/scheduled_workshops.html | 89 | ||||
-rw-r--r-- | workshop_app/templates/workshop_app/view_profile.html | 2 | ||||
-rw-r--r-- | workshop_app/templates/workshop_app/view_workshoptype_list.html | 2 | ||||
-rw-r--r-- | workshop_app/templates/workshop_app/workshop_stats.html | 245 | ||||
-rw-r--r-- | workshop_app/tests/test_views.py | 52 | ||||
-rw-r--r-- | workshop_app/urls.py | 2 | ||||
-rw-r--r-- | workshop_app/views.py | 238 |
16 files changed, 510 insertions, 178 deletions
diff --git a/workshop_app/forms.py b/workshop_app/forms.py index cce6dfc..1e9eb05 100644 --- a/workshop_app/forms.py +++ b/workshop_app/forms.py @@ -190,7 +190,6 @@ class ProposeWorkshopDateForm(forms.ModelForm): self.fields['proposed_workshop_title'].label = "Workshop :" self.fields['proposed_workshop_date'].label = "Workshop Date :" - class Meta: model = ProposeWorkshopDate exclude = ['status', 'proposed_workshop_instructor', @@ -198,5 +197,4 @@ class ProposeWorkshopDateForm(forms.ModelForm): widgets = { 'proposed_workshop_date': forms.DateInput(attrs={ 'class':'datepicker'}), - } - + }
\ No newline at end of file diff --git a/workshop_app/reminder_script.py b/workshop_app/reminder_script.py index 9b3f059..4c4350c 100644 --- a/workshop_app/reminder_script.py +++ b/workshop_app/reminder_script.py @@ -25,16 +25,15 @@ from workshop_portal.settings import ( from workshop_app.models import * from datetime import datetime, date - def send_email(): - tomorrow = date.today() + dt.timedelta(days=2) + upcoming = date.today() + dt.timedelta(days=2) upcoming_requested_workshops = RequestedWorkshop.objects.filter( - requested_workshop_date=tomorrow, + requested_workshop_date=upcoming, status='ACCEPTED' ) upcoming_proposed_workshops = ProposeWorkshopDate.objects.filter( - proposed_workshop_date=tomorrow, + proposed_workshop_date=upcoming, status='ACCEPTED' ) for w in upcoming_proposed_workshops: @@ -81,5 +80,4 @@ def send_email(): [w.requested_workshop_instructor.email], fail_silently=False ) - -send_email()
\ No newline at end of file +send_email() diff --git a/workshop_app/static/workshop_app/css/sticky-footer.css b/workshop_app/static/workshop_app/css/sticky-footer.css index 924258c..7e5ed4c 100644 --- a/workshop_app/static/workshop_app/css/sticky-footer.css +++ b/workshop_app/static/workshop_app/css/sticky-footer.css @@ -36,14 +36,16 @@ background-color: #efefef; } -/* Footer CSS */ +/** + * Footer Styles + */ + .footer { - position: relative; - bottom: 0; - right: 0; - left: 0; -} -.hiddenRow { - padding: 0 !important; + position: fixed; + right: 0; + bottom: 0; + left: 0; + background-color: #efefef; + text-align: center; }
\ No newline at end of file diff --git a/workshop_app/templates/workshop_app/base.html b/workshop_app/templates/workshop_app/base.html index 391fa9b..b2a5335 100644 --- a/workshop_app/templates/workshop_app/base.html +++ b/workshop_app/templates/workshop_app/base.html @@ -44,10 +44,10 @@ <h1>Base Template Content. Please override me</h1> {% endblock %} +</body> <footer class="footer"> <div class="container"> <p align="center">Developed by FOSSEE group, IIT Bombay</p> </div> </footer> -</body> </html>
\ No newline at end of file diff --git a/workshop_app/templates/workshop_app/edit_profile.html b/workshop_app/templates/workshop_app/edit_profile.html index 5e01430..70ad417 100644 --- a/workshop_app/templates/workshop_app/edit_profile.html +++ b/workshop_app/templates/workshop_app/edit_profile.html @@ -15,7 +15,7 @@ <li><a href="{{ URL_ROOT }}/create_workshop/">Create Workshop</a></li> <li><a href="{{ URL_ROOT }}/view_workshoptype_list/">Workshop List</a></li> <li><a href="{{ URL_ROOT }}/my_workshops/">My Workshops</a></li> - <li><a href="{{ URL_ROOT }}/scheduled_workshops/">Scheduled Workshops</a></li> + <li><a href="{{ URL_ROOT }}/workshop_stats/">Workshop Stats</a></li> </ul> {% else %} <div class="navbar-header"> diff --git a/workshop_app/templates/workshop_app/manage.html b/workshop_app/templates/workshop_app/manage.html index 9aac809..97f1604 100644 --- a/workshop_app/templates/workshop_app/manage.html +++ b/workshop_app/templates/workshop_app/manage.html @@ -10,7 +10,7 @@ <li ><a href="{{ URL_ROOT }}/create_workshop/">Create Workshop</a></li> <li ><a href="{{ URL_ROOT }}/view_workshoptype_list/"> Workshop List</a></li> <li ><a href="{{ URL_ROOT }}/my_workshops/">My Workshops</a></li> - <li><a href="{{ URL_ROOT }}/scheduled_workshops/">Scheduled Workshops</a></li> + <li><a href="{{ URL_ROOT }}/workshop_stats/">Workshop Stats</a></li> </ul> <ul class="nav navbar-nav navbar-right"> <li><a href="{{ URL_ROOT }}/view_profile/"><span class="glyphicon glyphicon-user"></span> Profile </a></li> @@ -126,4 +126,4 @@ </div> {% endif %} -{% endblock %}
\ No newline at end of file +{% endblock %} diff --git a/workshop_app/templates/workshop_app/my_workshops.html b/workshop_app/templates/workshop_app/my_workshops.html index c36485e..740e28f 100644 --- a/workshop_app/templates/workshop_app/my_workshops.html +++ b/workshop_app/templates/workshop_app/my_workshops.html @@ -49,7 +49,7 @@ <li><a href="{{ URL_ROOT }}/create_workshop/">Create Workshop</a></li> <li><a href="{{ URL_ROOT }}/view_workshoptype_list/">Workshop List</a></li> <li class="active"><a href="{{ URL_ROOT }}/my_workshops/">My Workshops</a></li> - <li><a href="{{ URL_ROOT }}/scheduled_workshops/">Scheduled Workshops</a></li> + <li><a href="{{ URL_ROOT }}/workshop_stats/">Workshop Stats</a></li> </ul> {% else %} <div class="navbar-header"> @@ -61,7 +61,7 @@ <li><a href="{{ URL_ROOT }}/faq/">FAQ's</a></li> <li><a href="{{ URL_ROOT }}/benefits/">Benefits</a></li> <li><a href="{{ URL_ROOT }}/view_workshoptype_details/"> Workshop List</a></li> - <li><a href="{{ URL_ROOT }}/how_to_participate/">How to Participate</a></li> + <li><a href="{{ URL_ROOT }}/how_to_participate/">How to Participate</a></li> </ul> {% endif %} @@ -81,7 +81,7 @@ <!-- Pending View --> <div class="container"> - <h3 align="center"><strong><u>Pending</u></strong></h3> + <h3 align="center"><strong><u>My Requests</u></strong></h3> <table class="table table-striped"> <thead> <tr> @@ -122,7 +122,7 @@ <!-- Accepted View --> <div class="container"> - <h3 align="center"><strong><u>Accepted</u></strong></h3> + <h3 align="center"><strong><u>Workshops Accepted</u></strong></h3> <table class="table table-striped"> <thead> <tr> @@ -171,7 +171,7 @@ <!-- Proposed View --> <div class="container"> - <h3 align="center"><strong><u>Proposed</u></strong></h3> + <h3 align="center"><strong><u>Proposed Workshops</u></strong></h3> <table class="table table-striped"> <thead> <tr> @@ -211,7 +211,7 @@ <!-- Deleted/Rejected View --> <div class="container"> - <h3 align="center"><strong><u>Deleted/Rejected</u></strong></h3> + <h3 align="center"><strong><u>Workshops Deleted/Rejected</u></strong></h3> <table class="table table-striped"> <thead> <tr> @@ -295,4 +295,4 @@ {% endif %} {% endif %} -{% endblock %}
\ No newline at end of file +{% endblock %} diff --git a/workshop_app/templates/workshop_app/profile_updated.html b/workshop_app/templates/workshop_app/profile_updated.html index e0aa3ad..abeb3de 100644 --- a/workshop_app/templates/workshop_app/profile_updated.html +++ b/workshop_app/templates/workshop_app/profile_updated.html @@ -15,7 +15,7 @@ <li><a href="{{ URL_ROOT }}/create_workshop/">Create Workshop</a></li> <li><a href="{{ URL_ROOT }}/view_workshoptype_details/"> Workshop List</a></li> <li><a href="{{ URL_ROOT }}/my_workshops/">My Workshops</a></li> - <li><a href="{{ URL_ROOT }}/scheduled_workshops/">Scheduled Workshops</a></li> + <li><a href="{{ URL_ROOT }}/workshop_stats/">Workshop Stats</a></li> </ul> {% else %} <div class="navbar-header"> diff --git a/workshop_app/templates/workshop_app/propose_workshop.html b/workshop_app/templates/workshop_app/propose_workshop.html index ce90dc8..95a4008 100644 --- a/workshop_app/templates/workshop_app/propose_workshop.html +++ b/workshop_app/templates/workshop_app/propose_workshop.html @@ -22,7 +22,7 @@ changeMonth: true, changeYear: true, minDate: dateToday, - maxDate: upto + maxDate: upto, // You can put more options here. }); }); diff --git a/workshop_app/templates/workshop_app/scheduled_workshops.html b/workshop_app/templates/workshop_app/scheduled_workshops.html deleted file mode 100644 index a9aedd6..0000000 --- a/workshop_app/templates/workshop_app/scheduled_workshops.html +++ /dev/null @@ -1,89 +0,0 @@ -{% extends 'workshop_app/base.html' %} - -{% block header %} - <nav class="navbar navbar-default navbar-custom"> - <div class="container-fluid"> - <div class="navbar-header"> - <a class="navbar-brand" href="{{ URL_ROOT }}/manage/">Home</a> - </div> - <ul class="nav navbar-nav"> - <li ><a href="{{ URL_ROOT }}/create_workshop/">Create Workshop</a></li> - <li ><a href="{{ URL_ROOT }}/view_workshoptype_list/"> Workshop List</a></li> - <li ><a href="{{ URL_ROOT }}/my_workshops/">My Workshops</a></li> - <li><a href="{{ URL_ROOT }}/scheduled_workshops/">Scheduled Workshops</a></li> - </ul> - <ul class="nav navbar-nav navbar-right"> - <li><a href="{{ URL_ROOT }}/view_profile/"><span class="glyphicon glyphicon-user"></span> Profile </a></li> - <li><a href="{{ URL_ROOT }}/logout/"><span class="glyphicon glyphicon-log-out"></span> Logout</a></li> - </ul> - </div> - </nav> -{% endblock %} - - -{% block content %} - - <div class="container"> - {% if scheduled_workshops %} - <h3>This Table Shows Scheduled Workshops which were Proposed By Coordinators</h3> - <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> - </tr> - </thead> - {% csrf_token %} - {% for workshop in proposed_workshops %} - {% if workshop.status == 'ACCEPTED' %} - <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> - </tr> - </tbody> - {% endif %} - {% endfor %} - </table> - <h3>This Table Shows Scheduled Workshops based on Instructors Approval </h3> - <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> - </tr> - </thead> - {% csrf_token %} - {% for workshop in requested_workshops %} - {% if workshop.status == 'ACCEPTED' %} - <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> - </tr> - </tbody> - {% endif %} - {% endfor %} - </table> - - {% else %} - <div class="jumbotron"> - <h2>Permission to View Scheduled Workshops is set to False</h2> - </div> - {% endif %} - </div> -{% endblock %} diff --git a/workshop_app/templates/workshop_app/view_profile.html b/workshop_app/templates/workshop_app/view_profile.html index d960f45..91efdf1 100644 --- a/workshop_app/templates/workshop_app/view_profile.html +++ b/workshop_app/templates/workshop_app/view_profile.html @@ -15,7 +15,7 @@ <li><a href="{{ URL_ROOT }}/create_workshop/">Create Workshop</a></li> <li><a href="{{ URL_ROOT }}/view_workshoptype_list/">Workshop List</a></li> <li><a href="{{ URL_ROOT }}/my_workshops/">My Workshops</a></li> - <li><a href="{{ URL_ROOT }}/scheduled_workshops/">Scheduled Workshops</a></li> + <li><a href="{{ URL_ROOT }}/workshop_stats/">Workshop Stats</a></li> </ul> {% else %} <div class="navbar-header"> diff --git a/workshop_app/templates/workshop_app/view_workshoptype_list.html b/workshop_app/templates/workshop_app/view_workshoptype_list.html index 53d32ce..75cee32 100644 --- a/workshop_app/templates/workshop_app/view_workshoptype_list.html +++ b/workshop_app/templates/workshop_app/view_workshoptype_list.html @@ -15,7 +15,7 @@ <ul class="nav navbar-nav"> {% if request.user.profile.position == 'instructor'%} <li><a href="{{ URL_ROOT }}/create_workshop/">Create Workshop</a></li> - <li><a href="{{ URL_ROOT }}/scheduled_workshops/">Scheduled Workshops</a></li> + <li><a href="{{ URL_ROOT }}/workshop_stats/">Workshop Stats</a></li> {% else %} <li><a href="{{ URL_ROOT }}/book/">Book</a></li> <li><a href="{{ URL_ROOT }}/propose_workshop/">Propose a Workshop</a></li> diff --git a/workshop_app/templates/workshop_app/workshop_stats.html b/workshop_app/templates/workshop_app/workshop_stats.html new file mode 100644 index 0000000..464c625 --- /dev/null +++ b/workshop_app/templates/workshop_app/workshop_stats.html @@ -0,0 +1,245 @@ +{% extends 'workshop_app/base.html' %} + +{% block header %} + <nav class="navbar navbar-default navbar-custom"> + <div class="container-fluid"> + <div class="navbar-header"> + <a class="navbar-brand" href="{{ URL_ROOT }}/manage/">Home</a> + </div> + <ul class="nav navbar-nav"> + <li ><a href="{{ URL_ROOT }}/create_workshop/">Create Workshop</a></li> + <li ><a href="{{ URL_ROOT }}/view_workshoptype_list/"> Workshop List</a></li> + <li ><a href="{{ URL_ROOT }}/my_workshops/">My Workshops</a></li> + <li class="active"><a href="{{ URL_ROOT }}/workshop_stats/" >Workshop Stats</a></li> + </ul> + <ul class="nav navbar-nav navbar-right"> + <li><a href="{{ URL_ROOT }}/view_profile/"><span class="glyphicon glyphicon-user"></span> Profile </a></li> + <li><a href="{{ URL_ROOT }}/logout/"><span class="glyphicon glyphicon-log-out"></span> Logout</a></li> + </ul> + </div> + </nav> +{% 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://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> + <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.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> + +<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 content %} + + <div class="container"> + {% if show_workshop_stats %} + <div align="right"> + <form method="POST" > + {% csrf_token %} + <div class="form-group"> + <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-warning" type="submit" name="Download" value="Download">Download</button> + <button class="btn btn-info" 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>Requested/Proposed By</th> + </tr> + </thead> + {% csrf_token %} + {% for workshop in upcoming_workshops %} + {% 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 upcoming_workshops.has_previous %} + <a class="page-link" tabindex="-1" + href="?page={{ upcoming_workshops.previous_page_number }}">Previous</a> + {% endif %} + </li> + <li class="page-item"> + <span class="current"> + Page {{ upcoming_workshops.number }} of {{ upcoming_workshops.paginator.num_pages }} + </span> + </li> + <li class="page-item"> + {% if upcoming_workshops.has_next %} + <a class="page-link" href="?page={{ upcoming_workshops.next_page_number }}">Next + </a> + {% endif %} + </li> + </ul> + </nav> + </div> + </div> + {% else %} + <div class="jumbotron"> + <h2>Permission to View Upcoming Workshops is set to false, please set it to true in settings.py</h2> + </div> + {% endif %} + </div> +<br> + <div class="row"> + <br> + <div class="col-md-12 "> + <br> + <canvas id="myChart" width="300px" height="40px"></canvas> + <script> + var ctx = document.getElementById("myChart").getContext('2d'); + var myChart = new Chart(ctx, { + type: 'bar', + data: { + labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', ' December'], + datasets: [{ + label: 'Number of workshops per Month for '+ dateToday.getFullYear(), + data: {{ workshop_count }}, + backgroundColor: [ + 'rgba(255, 99, 132, 0.2)', + 'rgba(54, 162, 235, 0.2)', + 'rgba(255, 206, 86, 0.2)', + 'rgba(75, 192, 192, 0.2)', + 'rgba(153, 102, 255, 0.2)', + 'rgba(242, 38, 19, 0.2)', + 'rgba(25, 91, 13, 0.2)', + 'rgba(54, 12, 235, 0.2)', + 'rgba(150, 40, 27, 0.2)', + 'rgba(66, 114, 155, 0.2)', + 'rgba(219, 10, 91, 0.2)', + 'rgba(191, 191, 191, 0.2)' + ], + borderColor: [ + 'rgba(255, 99, 132, 1)', + 'rgba(54, 162, 235, 1)', + 'rgba(255, 206, 86, 1)', + 'rgba(75, 192, 192, 1)', + 'rgba(153, 102, 255, 1)', + 'rgba(242, 38, 19, 1)', + 'rgba(25, 91, 13, 1)', + 'rgba(54, 12, 235, 1)', + 'rgba(150, 40, 27 ,1)', + 'rgba(66, 114, 155, 1)', + 'rgba(219, 10, 91, 1)', + 'rgba(191, 191, 191, 1)' + ], + borderWidth: 1 + }] + }, + options: { + responsive: true, + scales: { + yAxes: [{ + ticks: { + beginAtZero:true + } + }] + } + } + }); + </script> + </div> + + </div> +<br> +{% endblock %} diff --git a/workshop_app/tests/test_views.py b/workshop_app/tests/test_views.py index 5dbfff5..583efd2 100644 --- a/workshop_app/tests/test_views.py +++ b/workshop_app/tests/test_views.py @@ -10,6 +10,7 @@ from django.contrib.auth.models import Group, Permission from django.contrib.auth import authenticate from django.core.urlresolvers import reverse from workshop_app.forms import CreateWorkshop +from django.conf import settings class TestProfile(TestCase): def setUp(self): @@ -289,3 +290,54 @@ class TestStaticPages(TestCase): def test_view_workshoptype_details(self): response = self.client.get('/view_workshoptype_details/') self.assertEqual(response.status_code, 200) + + +class TestWorkshopStats(TestCase): + def setUp(self): + ''' + test user as instructor + ''' + self.superuser = User.objects.create_superuser( + username='admin', + password='pass@123', + email='test.user@gmail.com') + + self.mod_group = Group.objects.create(name='instructor') + + self.user_one = User.objects.create( + username='test_user1', + email='test.user@gmail.com') + + self.user_one.set_password('pass@123') + self.user_one.save() + + self.user_one_profile = Profile.objects.create( + user=self.user_one, + department='cs', + institute='IIT', + position='instructor', + phone_number='1122993388', + is_email_verified=1 + ) + + #Add user_one in instructor group and give required permissions + self.mod_group.user_set.add(self.user_one) + self.permission = (Permission.objects.all()) + self.user_one.user_permissions.add(self.permission[44]) + self.user_one.user_permissions.add(self.permission[43]) + self.user_one.user_permissions.add(self.permission[42]) + + def test_workshop_stats(self): + settings.SHOW_WORKSHOP_STATS = True + self.client.login(username=self.user_one, password='pass@123') + response = self.client.post('/workshop_stats/', + { + 'from': '2017-01-01', + 'to': '2017-12-31', + 'Download': 'download' + } + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.get('Content-Disposition'),'attachment;\ + filename="records_from_2017-01-01_to_2017-12-31.csv"') + diff --git a/workshop_app/urls.py b/workshop_app/urls.py index 6c2a04d..3d3b76a 100644 --- a/workshop_app/urls.py +++ b/workshop_app/urls.py @@ -44,7 +44,7 @@ urlpatterns = [ url(r'^view_workshoptype_details/$', views.view_workshoptype_details), url(r'^create_workshop/$', views.create_workshop), url(r'^propose_workshop/$', views.propose_workshop), - url(r'^scheduled_workshops/$', views.scheduled_workshops), + url(r'^workshop_stats/$', views.workshop_stats), url(r'^testimonials/$', views.testimonials), url(r'^file_view/(?P<workshop_title>[\w|\W]+)$', views.file_view), url(r'^jsi18n/$', django.views.i18n.javascript_catalog, js_info_dict), diff --git a/workshop_app/views.py b/workshop_app/views.py index 3bd6552..8e065f9 100644 --- a/workshop_app/views.py +++ b/workshop_app/views.py @@ -1,15 +1,16 @@ from .forms import ( - UserRegistrationForm, UserLoginForm, - ProfileForm, CreateWorkshop, - ProposeWorkshopDateForm - ) + UserRegistrationForm, UserLoginForm, + ProfileForm, CreateWorkshop, + ProposeWorkshopDateForm + ) from .models import ( - Profile, User, - has_profile, Workshop, - WorkshopType, RequestedWorkshop, - BookedWorkshop, ProposeWorkshopDate, - Testimonial - ) + Profile, User, + has_profile, Workshop, + WorkshopType, RequestedWorkshop, + BookedWorkshop, ProposeWorkshopDate, + Testimonial + ) +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 @@ -27,7 +28,10 @@ from textwrap import dedent from django.conf import settings from os import listdir, path, sep from zipfile import ZipFile +from django.db.models import Count +from django.contrib import messages import datetime as dt +import csv try: from StringIO import StringIO as string_io except ImportError: @@ -192,13 +196,13 @@ def book(request): for d in range(len(dates)): workshop_occurence = [ - dates[d].strftime("%d-%m-%Y"), - workshops.workshop_instructor, - workshops.workshop_title, - workshops.workshop_instructor_id, - workshops.workshop_title_id, - workshops.workshop_title.workshoptype_description - ] + dates[d].strftime("%d-%m-%Y"), + workshops.workshop_instructor, + workshops.workshop_title, + workshops.workshop_instructor_id, + workshops.workshop_title_id, + workshops.workshop_title.workshoptype_description + ] workshop_occurence_list.append(workshop_occurence) del workshop_occurence @@ -225,8 +229,8 @@ def book(request): #Objects of RequestedWorkshop for that particular coordinator rW_obj = RequestedWorkshop.objects.filter( - requested_workshop_coordinator=request.user - ) + requested_workshop_coordinator=request.user + ) for r in rW_obj: x = r.requested_workshop_date.strftime("%d-%m-%Y") for a in workshop_occurence_list: @@ -272,32 +276,32 @@ def book_workshop(request): queue = RequestedWorkshop.objects.filter( requested_workshop_instructor=client_data[1], requested_workshop_date=datetime.strptime( - client_data[0][2:], "%d-%m-%Y" - ), + client_data[0][2:], "%d-%m-%Y" + ), requested_workshop_title=client_data[-2] ).count() + 1 return HttpResponse(str(queue)) workshops_list = Workshop.objects.filter( - workshop_instructor=client_data[1], - workshop_title_id=client_data[2] - ) + workshop_instructor=client_data[1], + workshop_title_id=client_data[2] + ) today = datetime.now() + dt.timedelta(days=3) upto = datetime.now() + dt.timedelta(weeks=52) for workshop in workshops_list: workshop_recurrence_list = workshop.recurrences.between( - today, - upto, - inc=True - ) + today, + upto, + inc=True + ) rW_obj = RequestedWorkshop() if RequestedWorkshop.objects.filter( requested_workshop_instructor=workshop.workshop_instructor, requested_workshop_date=datetime.strptime( - client_data[0][2:], "%d-%m-%Y", - ), + client_data[0][2:], "%d-%m-%Y", + ), requested_workshop_coordinator=request.user, requested_workshop_title=client_data[-1] ).count() > 0: @@ -312,8 +316,8 @@ def book_workshop(request): rW_obj.requested_workshop_instructor = workshop.workshop_instructor rW_obj.requested_workshop_coordinator = request.user rW_obj.requested_workshop_date = datetime.strptime( - workshop_date,"%d-%m-%Y" - ) + workshop_date,"%d-%m-%Y" + ) rW_obj.requested_workshop_title = workshop.workshop_title rW_obj.save() @@ -361,8 +365,8 @@ def manage(request): try: #Can Handle Multiple Workshops workshop_details = Workshop.objects.filter( - workshop_instructor=user.id - ) + workshop_instructor=user.id + ) workshop_occurence_list = [] today = datetime.now() + dt.timedelta(days=3) @@ -866,11 +870,12 @@ def file_view(request, workshop_title): zipfile_name.seek(0) response = HttpResponse(content_type='application/zip') response['Content-Disposition'] = 'attachment; filename={0}.zip'.format( - filename.workshoptype_name.replace(" ", "_") - ) + filename.workshoptype_name.replace(" ", "_") + ) response.write(zipfile_name.read()) return response + def testimonials(request): testimonials = Testimonial.objects.all().order_by('-id') paginator = Paginator(testimonials, 3) #Show upto 12 workshops per page @@ -884,42 +889,163 @@ def testimonials(request): except EmptyPage: #If page is out of range(e.g 999999), deliver last page. messages = paginator.page(paginator.num_pages) - return render(request, 'workshop_app/testimonals.html', {"messages":messages}) + return render(request, 'workshop_app/testimonals.html', + {"messages":messages}) + + +def check_workshop_type(x): + try: + y = datetime.strftime(x.proposed_workshop_date, '%d-%m-%Y') + except: + y = datetime.strftime(x.requested_workshop_date, '%d-%m-%Y') + return y + @login_required -def scheduled_workshops(request): +def workshop_stats(request): user = request.user today = datetime.now() - upto = datetime.now() + dt.timedelta(days=15) + upto = today + dt.timedelta(days=15) + + #For Monthly Chart + workshop_count = [0] * 12 + for x in range(12): + workshop_count[x-1] +=RequestedWorkshop.objects.filter( + requested_workshop_date__year=str(today.year), + requested_workshop_date__month=str(x)).count() + workshop_count[x-1] +=ProposeWorkshopDate.objects.filter( + proposed_workshop_date__year=str(today.year), + proposed_workshop_date__month=str(x)).count() + + 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' + ] + + 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 + ] + + except: + 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 + ] + + writer.writerow(row) + return response + else: + return render(request, 'workshop_app/workshop_stats.html', + { + "upcoming_workshops": upcoming_workshops, + "show_workshop_stats": settings.SHOW_WORKSHOP_STATS, + "workshop_count": workshop_count + }) + except: + 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' - ) - proposed_workshops = (sorted(proposed_workshops, - key=lambda x: datetime.strftime( - x.proposed_workshop_date, '%d-%m-%Y' - ))) - #Fetches Accepted workshops which were Accepted by Instructors based on their Availability + ) + + #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' ) - requested_workshops = (sorted(requested_workshops, - key=lambda x: datetime.strftime( - x.request_workshop_date, '%d-%m-%Y' - ))) + + 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: - proposed_workshops = None - requested_workshops = None - return render(request, 'workshop_app/scheduled_workshops.html', + upcoming_workshops = None + + 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, 'workshop_app/workshop_stats.html', { - "proposed_workshops": proposed_workshops, - "requested_workshops": requested_workshops, - "scheduled_workshops": settings.SCHEDULED_WORKSHOPS + "upcoming_workshops": upcoming_workshops, + "show_workshop_stats": settings.SHOW_WORKSHOP_STATS, + "workshop_count": workshop_count }) else: - redirect('/book/') + redirect('/manage/') |