Commit 160dc1e7e530ea07d92f5c3167d7e5b94dd89a6d

Authored by Jailson Dias
2 parents 0d54effa bcf2559a

Merge branch 'refactoring' of https://github.com/amadeusproject/amadeuslms into refactoring

analytics/static/.sass-cache/9bdb779ec82a4a96f72be09b83b7c997addd0129/general.sassc
No preview for this file type
analytics/static/analytics/general.css
... ... @@ -62,4 +62,39 @@
62 62 .chart h4 {
63 63 color: #009688; }
64 64  
  65 +.selector {
  66 + width: 90%;
  67 + height: 20%;
  68 + color: white;
  69 + padding-left: 5px;
  70 + background-color: #009688;
  71 + margin-top: 10px;
  72 + border-radius: 0px 20px 20px 0px; }
  73 + .selector p {
  74 + padding-top: 10px;
  75 + font-size: 16px; }
  76 +
  77 +.middle-chart article {
  78 + text-align: center; }
  79 +
  80 +.user-tooltip {
  81 + background: linear-gradient(#0e8999, #6bf0ce);
  82 + color: white;
  83 + border-radius: 10px;
  84 + padding: 7px; }
  85 +
  86 +.most-accessed-list {
  87 + background-color: #52b7bd;
  88 + width: 80%;
  89 + margin-left: 2%;
  90 + margin-top: 2%;
  91 + padding-left: 0;
  92 + color: white;
  93 + transition: width 2s, height 2s, background-color 2s, transform 2s; }
  94 + .most-accessed-list li {
  95 + padding-left: 1%; }
  96 +
  97 +.most-accessed-item:hover {
  98 + background-color: #3aa7ad; }
  99 +
65 100 /*# sourceMappingURL=general.css.map */
... ...
analytics/static/analytics/general.css.map
1 1 {
2 2 "version": 3,
3   -"mappings": "AAEA,YAAY;EACR,MAAM,EAAE,iBAAiB;EACzB,aAAa,EAAE,IAAI;EACnB,OAAO,EAAE,IAAI;EACb,QAAQ,EAAE,IAAI;EACd,KAAK,EAAE,IAAI;;AAEf,sBAAsB;EAClB,MAAM,EAAE,GAAG;EACX,OAAO,EAAE,KAAK;EACd,yBAAE;IACE,KAAK,EAbC,OAAO;EAejB,yBAAE;IACE,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE,WAAW;IACpB,KAAK,EAAE,GAAG;IACV,MAAM,EAAE,GAAG;IAEX,4BAAE;MACE,YAAY,EAAE,EAAE;MAChB,KAAK,EAAE,GAAG;MACV,KAAK,EAAE,KAAK;MAEZ,yCAAY;QACR,gBAAgB,EAAE,OAAO;QACzB,KAAK,EAAE,IAAI;QACX,UAAU,EAAE,MAAM;QAClB,aAAa,EAAE,IAAI;MAEvB,gCAAG;QACC,gBAAgB,EAAE,OAAO;QACzB,KAAK,EAAE,IAAI;QACX,UAAU,EAAE,MAAM;QAClB,aAAa,EAAE,IAAI;;AAInC,sBAAsB;EAClB,UAAU,EAAE,2CAA2C;EACvD,MAAM,EAAE,IAAI;EACZ,aAAa,EAAE,IAAI;EACnB,KAAK,EAAE,OAAO;EACd,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,IAAI;EAEhB,yBAAE;IACE,WAAW,EAAE,EAAE;;AAEvB,eAAe;EACX,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI;;AAEhB,UAAU;EACN,aAAa,EAAE,IAAI;EACnB,KAAK,EAAE,OAAO;EACd,KAAK,EAAE,IAAI;;AAIf,SAAS;EACL,KAAK,EAAE,OAAO;EACd,SAAS,EAAE,IAAI;;AAGnB,mBAAmB;EACf,UAAU,EAAE,iCAAmC;;AAEnD,MAAM;EACF,KAAK,EAAE,GAAG;EACV,KAAK,EAAE,IAAI;EACX,SAAE;IACE,KAAK,EA1EC,OAAO",
  3 +"mappings": "AAEA,YAAY;EACR,MAAM,EAAE,iBAAiB;EACzB,aAAa,EAAE,IAAI;EACnB,OAAO,EAAE,IAAI;EACb,QAAQ,EAAE,IAAI;EACd,KAAK,EAAE,IAAI;;AAEf,sBAAsB;EAClB,MAAM,EAAE,GAAG;EACX,OAAO,EAAE,KAAK;EACd,yBAAE;IACE,KAAK,EAbC,OAAO;EAejB,yBAAE;IACE,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE,WAAW;IACpB,KAAK,EAAE,GAAG;IACV,MAAM,EAAE,GAAG;IAEX,4BAAE;MACE,YAAY,EAAE,EAAE;MAChB,KAAK,EAAE,GAAG;MACV,KAAK,EAAE,KAAK;MAEZ,yCAAY;QACR,gBAAgB,EAAE,OAAO;QACzB,KAAK,EAAE,IAAI;QACX,UAAU,EAAE,MAAM;QAClB,aAAa,EAAE,IAAI;MAEvB,gCAAG;QACC,gBAAgB,EAAE,OAAO;QACzB,KAAK,EAAE,IAAI;QACX,UAAU,EAAE,MAAM;QAClB,aAAa,EAAE,IAAI;;AAInC,sBAAsB;EAClB,UAAU,EAAE,2CAA2C;EACvD,MAAM,EAAE,IAAI;EACZ,aAAa,EAAE,IAAI;EACnB,KAAK,EAAE,OAAO;EACd,WAAW,EAAE,GAAG;EAChB,UAAU,EAAE,IAAI;EAEhB,yBAAE;IACE,WAAW,EAAE,EAAE;;AAEvB,eAAe;EACX,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI;;AAEhB,UAAU;EACN,aAAa,EAAE,IAAI;EACnB,KAAK,EAAE,OAAO;EACd,KAAK,EAAE,IAAI;;AAIf,SAAS;EACL,KAAK,EAAE,OAAO;EACd,SAAS,EAAE,IAAI;;AAGnB,mBAAmB;EACf,UAAU,EAAE,iCAAmC;;AAEnD,MAAM;EACF,KAAK,EAAE,GAAG;EACV,KAAK,EAAE,IAAI;EACX,SAAE;IACE,KAAK,EA1EC,OAAO;;AA4ErB,SAAS;EACL,KAAK,EAAE,GAAG;EACV,MAAM,EAAE,GAAG;EACX,KAAK,EAAE,KAAK;EACZ,YAAY,EAAE,GAAG;EACjB,gBAAgB,EAAE,OAAO;EACzB,UAAU,EAAE,IAAI;EAChB,aAAa,EAAE,iBAAiB;EAChC,WAAC;IACG,WAAW,EAAE,IAAI;IACjB,SAAS,EAAE,IAAI;;AAInB,qBAAO;EACH,UAAU,EAAE,MAAM;;AAE1B,aAAa;EACT,UAAU,EAAE,iCAAkC;EAC9C,KAAK,EAAE,KAAK;EACZ,aAAa,EAAE,IAAI;EACnB,OAAO,EAAE,GAAG;;AAEhB,mBAAmB;EACf,gBAAgB,EAAE,OAAO;EACzB,KAAK,EAAE,GAAG;EACV,WAAW,EAAE,EAAE;EACf,UAAU,EAAE,EAAE;EACd,YAAY,EAAE,CAAC;EACf,KAAK,EAAE,KAAK;EACZ,UAAU,EAAE,sDAAsD;EAElE,sBAAE;IACE,YAAY,EAAE,EAAE;;AAExB,yBAAyB;EACrB,gBAAgB,EAAE,OAAO",
4 4 "sources": ["general.sass"],
5 5 "names": [],
6 6 "file": "general.css"
... ...
analytics/static/analytics/general.sass
... ... @@ -72,4 +72,42 @@ $title-color: #009688
72 72 width: 30%
73 73 float: left
74 74 h4
75   - color: $title-color
76 75 \ No newline at end of file
  76 + color: $title-color
  77 +
  78 +.selector
  79 + width: 90%
  80 + height: 20%
  81 + color: white
  82 + padding-left: 5px
  83 + background-color: #009688
  84 + margin-top: 10px
  85 + border-radius: 0px 20px 20px 0px
  86 + p
  87 + padding-top: 10px
  88 + font-size: 16px
  89 +
  90 +
  91 +.middle-chart
  92 + article
  93 + text-align: center
  94 +
  95 +.user-tooltip
  96 + background: linear-gradient( #0e8999, #6bf0ce)
  97 + color: white
  98 + border-radius: 10px
  99 + padding: 7px
  100 +
  101 +.most-accessed-list
  102 + background-color: #52b7bd
  103 + width: 80%
  104 + margin-left: 2%
  105 + margin-top: 2%
  106 + padding-left: 0
  107 + color: white
  108 + transition: width 2s, height 2s, background-color 2s, transform 2s
  109 +
  110 + li
  111 + padding-left: 1%
  112 +
  113 +.most-accessed-item:hover
  114 + background-color: #3aa7ad
77 115 \ No newline at end of file
... ...
analytics/static/analytics/js/behavior.js 0 → 100644
... ... @@ -0,0 +1,75 @@
  1 +
  2 +$(document).ready(function(){
  3 + selectors_options.init();
  4 +});
  5 +
  6 +
  7 +var selectors_options = {
  8 + init: function(){
  9 + selectors = $("div.selector");
  10 + selectors.click(function(e){
  11 + selectors_options.loadData(e.currentTarget);
  12 + });
  13 + },
  14 + loadData: function(e){
  15 + if (e){
  16 + opened = $(e).attr('opened');
  17 + if (typeof opened !== typeof undefined && opened !== false){
  18 + selectors_options.deleteChildren(e);
  19 + }else {
  20 + switch(e.attributes['data-url'].value){
  21 + case "subjects":
  22 + var url = "/analytics/most_accessed_subjects";
  23 + break;
  24 + case "categories":
  25 + var url = "/analytics/most_accessed_categories";
  26 + break;
  27 + case "resources":
  28 + var url = "/analytics/most_accessed_resources";
  29 + break;
  30 +
  31 + }
  32 +
  33 + }
  34 + }
  35 + if(url){
  36 + $.get(url, function(dataset){
  37 + return dataset;
  38 + }).done(function(data){
  39 + selectors_options.modifyElement(e, data);
  40 +
  41 + }).fail(function(error){
  42 + console.log("couldn't complete get request");
  43 + });
  44 + }
  45 +
  46 +
  47 + },
  48 + modifyElement: function(e, data){
  49 + var string_build = "";
  50 + string_build += '<ul class="most-accessed-list" style="display:none;">';
  51 +
  52 + data.forEach(function(datum){
  53 + string_build += '<li class="most-accessed-item">' +datum.name+ ' ' + datum.count+ '</li>';
  54 + });
  55 + string_build += "</ul>";
  56 +
  57 + $(e).after(string_build);
  58 + var new_elem = $(e).next();
  59 + new_elem.slideDown();
  60 + $(e).attr("opened", true);
  61 +
  62 + },
  63 + deleteChildren: function(e){
  64 + console.log("delete children");
  65 + var most_accessed_list = $(e).next();
  66 + $(most_accessed_list).animate(
  67 + {height: 0,
  68 + opacity: 0.1
  69 + }, 1000, function(){
  70 + $(this).remove(); //remove list from UI
  71 + });
  72 +
  73 + $(e).removeAttr("opened"); //remove attribute so it can call API again
  74 + },
  75 +};
... ...
analytics/static/analytics/js/charts.js
... ... @@ -91,7 +91,7 @@ var charts = {
91 91  
92 92 build_bubble_user: function(url){
93 93 $.get(url, function(dataset){
94   - var width = 600;
  94 + var width = 300;
95 95 var height = 300;
96 96  
97 97  
... ... @@ -121,7 +121,7 @@ var charts = {
121 121  
122 122 var color = d3.scaleOrdinal(d3.schemeCategory20);
123 123 //adding new div to carousel-inner div
124   - var new_div = d3.select(".carousel-inner").append("div").attr("class","item");
  124 + var new_div = d3.select(".middle-chart").append("div").attr("class","item");
125 125 var radiusScale = d3.scaleSqrt().domain([min(), max()]).range([10,50]);
126 126 var svg = new_div.append("svg").attr("width", width).attr("height", height)
127 127 .style("margin","auto")
... ... @@ -131,17 +131,7 @@ var charts = {
131 131 .attr("width", width)
132 132 .attr("height", height);
133 133  
134   - //adding svg title
135   -
136   - svg.append("text")
137   - .attr("text-anchor", "middle")
138   - .attr("x", width/2 )
139   - .attr("y", 30)
140   - .style("font-size", "30px")
141   - .text("Usuários mais ativos no Amadeus")
142   - .attr("fill", "#003333")
143   - .style("font-weight", "bold")
144   - .style("font-style", "italic");
  134 +
145 135  
146 136 var simulation = d3.forceSimulation()
147 137 .force("x", d3.forceX(width/2).strength(0.05))
... ... @@ -158,6 +148,29 @@ var charts = {
158 148 .append("g")
159 149 .attr("class",".user-dot");
160 150  
  151 +
  152 + var defs = groups.append('svg:defs');
  153 +
  154 + var gradient = defs.append("linearGradient")
  155 + .attr("id", "svgGradient")
  156 + .attr("x1", "0%")
  157 + .attr("x2", "100%")
  158 + .attr("y1", "0%")
  159 + .attr("y2", "100%");
  160 +
  161 + gradient.append("stop")
  162 + .attr('class', 'start')
  163 + .attr("offset", "0%")
  164 + .attr("stop-color", "#007991")
  165 + .attr("stop-opacity", 1);
  166 +
  167 + gradient.append("stop")
  168 + .attr('class', 'end')
  169 + .attr("offset", "100%")
  170 + .attr("stop-color", "#78ffd6")
  171 + .attr("stop-opacity", 1);
  172 +
  173 +
161 174 //Create circles to be drawn
162 175 var circles = groups
163 176 .append('circle')
... ... @@ -167,30 +180,47 @@ var charts = {
167 180  
168 181 .attr("fill", function(d){
169 182 return 'url('+'#'+'user_'+d['user_id']+')';
170   - });
  183 + })
  184 + .attr("stroke", "url(#svgGradient)") //using id setted by the svg
  185 + .attr("stroke-width", 3);
171 186  
172 187  
173 188  
174 189 //Add texts to show user names
175   - groups.append("text")
  190 + /*groups.append("text")
176 191 .text(function(d){
177 192 return d['user'] +'('+ d['count'] + ')';
178   - }).attr("fill", "#FFFFFF")
  193 + }).attr("fill", "#000000")
179 194 .attr("id", function(d){
180 195 return "user_tooltip_"+d['user_id'];
181   - }).style("display", "none");
  196 + }).style("display", "none");*/
  197 +
  198 + var tooltip_div = d3.select("body").append("div")
  199 + .attr('class','user-tooltip')
  200 + .attr("display", "none")
  201 + .attr("height", 28)
  202 + .attr("width", 80)
  203 + .style("position", "absolute")
  204 + .style('pointer-events', 'none');
  205 +
  206 +
182 207  
183 208  
184 209 groups.on("mouseover", function(d){
185   - $("#"+"user_tooltip_"+d['user_id']).show();
  210 + //$("#"+"user_tooltip_"+d['user_id']).show();
  211 + tooltip_div.transition().duration(200).style("opacity", .9);
  212 + tooltip_div.html(d['user'] + '</br>' + d['count'] + ' acessos')
  213 + .style("left", (d3.event.pageX) + "px")
  214 + .style("top", (d3.event.pageY - 28) + "px");
  215 + console.log(d3.event.pageX);
  216 + console.log(d3.event.pageY);
186 217 });
187 218  
188 219  
189 220 groups.on("mouseout", function(d){
190   - $("#"+"user_tooltip_"+d['user_id']).hide();
  221 + //$("#"+"user_tooltip_"+d['user_id']).hide();
191 222 });
192 223  
193   - var defs = groups.append('svg:defs');
194 224  
195 225 //Attching images to bubbles
196 226 defs.append("svg:pattern")
... ... @@ -217,11 +247,13 @@ var charts = {
217 247 .attr("y", 0);
218 248  
219 249  
  250 +
220 251  
221 252 //simulation
222 253 simulation.nodes(dataset)
223 254 .on('tick', ticked); //so all data points are attached to it
224 255  
  256 + console.log("finished simulation");
225 257 function ticked(){
226 258 groups.attr("transform", function(d){
227 259 return "translate(" + d.x + "," + d.y + ")";
... ... @@ -234,10 +266,10 @@ var charts = {
234 266  
235 267 most_accessed_subjects: function(url){
236 268 $.get(url, function(dataset){
237   - var width = 800;
  269 + var width = 200;
238 270 var height = 300;
239 271  
240   - var new_div = d3.select(".carousel-inner").append("div").attr("class","item");
  272 + var new_div = d3.select(".carousel-inner").append("div").attr("test","ok");
241 273  
242 274 var svg = new_div.append("svg").attr("width", "100%").attr("height", height)
243 275 .style("margin","auto")
... ... @@ -417,7 +449,10 @@ var charts = {
417 449 }
418 450 }
419 451  
420   -charts.most_used_tags('/analytics/most_used_tags');
421   -//charts.build_resource('/topics/count_resources/');
422   -//charts.build_bubble_user('/users/get_users_log/');
423   -//charts.most_accessed_subjects('/subjects/most_accessed_subjects');
424 452 \ No newline at end of file
  453 +
  454 +$(document).ready(function(){
  455 + charts.most_used_tags('/analytics/most_used_tags');
  456 + //charts.build_resource('/topics/count_resources/');
  457 + charts.build_bubble_user('/analytics/most_active_users/');
  458 + //charts.most_accessed_subjects('/subjects/most_accessed_subjects');
  459 +});
... ...
analytics/templates/analytics/general.html
... ... @@ -8,6 +8,7 @@
8 8  
9 9 {% block javascript %}
10 10 <script type="text/javascript" src="{% static "analytics/js/charts.js" %}"></script>
  11 + <script type="text/javascript" src=" {% static "analytics/js/behavior.js" %} "></script>
11 12 {% endblock javascript %}
12 13  
13 14 {% block breadcrumbs %}
... ... @@ -70,18 +71,19 @@
70 71 </div>
71 72  
72 73 <div id="left-data-selector">
73   - <ul>
74   - <li>
75   - {% trans "most accessed categories" %}
76   - </li>
77   - <li>
78   - {% trans "most accessed subjects" %}
79   - </li>
80   - <li>
81   - {% trans "most accessed resource" %}
82   - </li>
83   - </ul>
84   -
  74 +
  75 + <div class="selector" data-url="categories">
  76 + <p> {% trans "most accessed categories" %} </p>
  77 + </div>
  78 +
  79 + <div class="selector" data-url="subjects">
  80 + <p> {% trans "most accessed subjects" %} </p>
  81 + </div>
  82 +
  83 + <div class="selector" data-url="resources">
  84 + <p> {% trans "most accessed resource" %} </p>
  85 + </div>
  86 +
85 87 </div>
86 88  
87 89 </div>
... ... @@ -89,6 +91,7 @@
89 91 <article>
90 92 <h4>{% trans "most active users" %}</h4>
91 93 </article>
  94 +
92 95 </div>
93 96 <div class="chart right-chart">
94 97  
... ...
analytics/tests/test_general_dashboard.py
... ... @@ -88,7 +88,7 @@ class APIDashBoardTest(TestCase):
88 88  
89 89  
90 90 def test_most_accessed_categories(self):
91   - self.fail("finish the test")
  91 + self.fail("finish test on categories")
92 92  
93 93 def test_most_active_users(self):
94 94 self.fail("finish the test")
... ...
analytics/urls.py
... ... @@ -8,4 +8,7 @@ urlpatterns = [
8 8 #"api" callls
9 9 url(r'^most_used_tags/$', views.most_used_tags, name="most_used_tags"),
10 10 url(r'^most_accessed_subjects/$', views.most_accessed_subjects, name="most_accessed_subjects"),
  11 + url(r'^most_accessed_categories/$', views.most_accessed_categories, name = "most_accessed_categories"),
  12 + url(r'^most_accessed_resources/$', views.most_accessed_resource_kind, name= "most_accessed_resources"),
  13 + url(r'^most_active_users/$', views.most_active_users, name= "most_active_users"),
11 14 ]
12 15 \ No newline at end of file
... ...
analytics/views.py
... ... @@ -79,15 +79,38 @@ def most_accessed_subjects(request):
79 79 def most_accessed_categories(request):
80 80 data = {}
81 81  
82   - data = Log.objects.filter('category')
  82 + data = Log.objects.filter(resource = 'category')
83 83 categories = {}
84 84 for datum in data:
85 85 if datum.context:
86   - pass
87   - return None
  86 + category_id = datum.context['category_id']
  87 + if category_id in categories.keys():
  88 + categories[category_id]['count'] = categories[category_id]['count'] + 1
  89 + else:
  90 + categories[category_id] = {'name': datum.context['category_name'], 'count': 1 }
  91 +
  92 + categories = sorted(categories.values(), key = lambda x: x['count'], reverse = True)
  93 + categories = categories[:5]
  94 + return JsonResponse(categories, safe= False)
88 95  
89 96 def most_accessed_resource_kind(request):
90   - return None
  97 + resources_names = [cls.__name__ for cls in Resource.__subclasses__()]
  98 + print(resources_names)
  99 + resources = {}
  100 +
  101 + mapping = {}
  102 + mapping['pdffile'] = str(_('PDF File'))
  103 + mapping['goals'] = str(_('Topic Goals'))
  104 + mapping['link'] = str(_('Link to Website'))
  105 + mapping['filelink'] = str(_('File Link'))
  106 + mapping['webconference'] = str(_('Web Conference'))
  107 + mapping['ytvideo'] = str(_('YouTube Video'))
  108 + mapping['webpage'] = str(_('WebPage'))
  109 +
  110 +
  111 +
  112 +
  113 + return JsonResponse(resources, safe = False)
91 114  
92 115  
93 116 def most_active_users(request):
... ...
subjects/templates/subjects/backup.html
... ... @@ -56,6 +56,7 @@
56 56 <li><a href="{% url 'subjects:update' subject.slug %}"><i class="fa fa-pencil fa-fw" aria-hidden="true"></i>{% trans 'Edit' %}</a></li>
57 57 <li><a href="{% url 'groups:index' subject.slug %}"><i class="fa fa-group fa-fw" aria-hidden="true"></i>{% trans 'Groups' %}</a></li>
58 58 <li><a href="{% url 'subjects:backup' subject.slug %}"><i class="fa fa-database fa-fw" aria-hidden="true"></i>{% trans 'Backup' %}</a></li>
  59 + <li><a href="{% url 'subjects:restore' subject.slug %}"><i class="fa fa-recycle fa-fw" aria-hidden="true"></i>{% trans 'Restore' %}</a></li>
59 60 <li><a href="javascript:delete_subject.get('{% url 'subjects:delete' subject.slug %}?view=index','#subject','#modal_subject')"><i class="fa fa-trash fa-fw" aria-hidden="true"></i>&nbsp;{% trans 'Remove' %}</a></li>
60 61 </ul>
61 62 {% endif %}
... ... @@ -161,12 +162,5 @@
161 162 }
162 163 });
163 164 });
164   -
165   - // $("#bkp_form").submit(function () {
166   - // setTimeout(function() {
167   - // // Should be triggered after download started
168   - // document.location.href="{% url 'subjects:view' subject.slug %}";
169   - // }, 3000);
170   - // });
171 165 </script>
172 166 {% endblock content %}
... ...
subjects/templates/subjects/restore.html 0 → 100644
... ... @@ -0,0 +1,138 @@
  1 +{% extends 'categories/home.html' %}
  2 +
  3 +{% load static i18n pagination permissions_tags subject_counter chat_tags %}
  4 +{% load django_bootstrap_breadcrumbs %}
  5 +
  6 +{% block javascript%}
  7 + {{ block.super }}
  8 + <script type="text/javascript" src="{% static 'subjects/js/modal_subject.js' %}"></script>
  9 +{% endblock%}
  10 +
  11 +{% block breadcrumbs %}
  12 + {{ block.super }}
  13 + {% breadcrumb subject.category 'subjects:cat_view' subject.category.slug %}
  14 + {% breadcrumb subject 'subjects:view' subject.slug %}
  15 +
  16 + {% trans "Restore" as bread %}
  17 + {% breadcrumb bread 'subjects:restore' subject.slug %}
  18 +{% endblock %}
  19 +
  20 +{% block content %}
  21 + {% if messages %}
  22 + {% for message in messages %}
  23 + <div class="alert alert-{{ message.tags }} alert-dismissible" role="alert">
  24 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  25 + <span aria-hidden="true">&times;</span>
  26 + </button>
  27 + <p>{{ message }}</p>
  28 + </div>
  29 + {% endfor %}
  30 + {% endif %}
  31 +
  32 + {% subject_permissions request.user subject as has_subject_permissions %}
  33 +
  34 + {% if subject.visible %}
  35 + <div class="panel panel-info subject-panel" id="subject_{{subject.slug}}">
  36 + <div class="panel-heading">
  37 + {% elif has_subject_permissions %}
  38 + <div class="panel panel-info subject-panel-invisible" id="subject_{{subject.slug}}">
  39 + <div class="panel-heading panel-invisible">
  40 + {% endif %}
  41 + <div class="row">
  42 + <div class="col-md-12 category-header">
  43 + <h4 class="panel-title" style="margin-top: 10px; margin-bottom: 8px">
  44 + <span>{{ subject.name }} / {% trans "Restore" %}</span>
  45 + </h4>
  46 +
  47 + <div class="col-md-5 pull-right category-card-items">
  48 + {% if request.user in subject.professor.all or request.user in subject.category.coordinators.all or request.user.is_staff %}
  49 + <a href="" id="moreActions" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
  50 + <i class="fa fa-ellipsis-v" aria-hidden="true"></i>
  51 + </a>
  52 + <ul class="dropdown-menu pull-right" aria-labelledby="moreActions">
  53 + {% if request.user not in subject.professor.all %}
  54 + <li><a href="{% url 'subjects:replicate' subject.slug %}"><i class="fa fa-files-o fa-fw" aria-hidden="true"></i>{% trans 'Replicate' %}</a></li>
  55 + {% endif %}
  56 + <li><a href="{% url 'subjects:update' subject.slug %}"><i class="fa fa-pencil fa-fw" aria-hidden="true"></i>{% trans 'Edit' %}</a></li>
  57 + <li><a href="{% url 'groups:index' subject.slug %}"><i class="fa fa-group fa-fw" aria-hidden="true"></i>{% trans 'Groups' %}</a></li>
  58 + <li><a href="{% url 'subjects:backup' subject.slug %}"><i class="fa fa-database fa-fw" aria-hidden="true"></i>{% trans 'Backup' %}</a></li>
  59 + <li><a href="{% url 'subjects:restore' subject.slug %}"><i class="fa fa-recycle fa-fw" aria-hidden="true"></i>{% trans 'Restore' %}</a></li>
  60 + <li><a href="javascript:delete_subject.get('{% url 'subjects:delete' subject.slug %}?view=index','#subject','#modal_subject')"><i class="fa fa-trash fa-fw" aria-hidden="true"></i>&nbsp;{% trans 'Remove' %}</a></li>
  61 + </ul>
  62 + {% endif %}
  63 + </div>
  64 + </div>
  65 + </div>
  66 + </div>
  67 + <div id="{{subject.slug}}" class="panel-collapse in collapse category-panel-content" style="position: relative">
  68 + <h5>{% trans "Please select below the file you want to use for restore:" %}</h5>
  69 +
  70 + <div class="backup_container">
  71 + <form id="restore_form" method="post" action="{% url 'subjects:do_restore' subject.slug %}" enctype="multipart/form-data">
  72 + {% csrf_token %}
  73 +
  74 + <div class="form-group is-fileinput">
  75 + <input type="file" id="zip_file" name="zip_file" class="form-control" accept=".zip" />
  76 +
  77 + <div class="input-group common-file-input">
  78 + <input type="text" readonly="" class="form-control" placeholder="{% trans 'Choose your file...' %}">
  79 + <span class="input-group-btn input-group-sm">
  80 + <button type="button" class="btn btn-fab btn-fab-mini">
  81 + <i class="material-icons">attach_file</i>
  82 + </button>
  83 + </span>
  84 + </div>
  85 +
  86 + <div class="filedrag">
  87 + {% trans 'Click or drop the file here' %}<br />
  88 + </div>
  89 + </div>
  90 +
  91 + <div class="row text-center">
  92 + <input type="submit" value="{% trans 'Restore' %}" class="btn btn-success btn-raised" />
  93 + </div>
  94 + </form>
  95 + </div>
  96 + </div>
  97 + </div>
  98 +
  99 + <script type="text/javascript">
  100 + $(function () {
  101 + $.material.init();
  102 +
  103 + if (window.File && window.FileList && window.FileReader) {
  104 + Init();
  105 + }
  106 + });
  107 +
  108 + // initialize
  109 + function Init() {
  110 + var small = $("#zip_file"),
  111 + filedrag = $(".filedrag"),
  112 + common = $(".common-file-input");
  113 +
  114 + // file select
  115 + small.on("change", FileSelectHandler);
  116 +
  117 + // is XHR2 available?
  118 + var xhr = new XMLHttpRequest();
  119 + if (xhr.upload) {
  120 + // file drop
  121 + filedrag.on("drop", FileSelectHandler);
  122 + filedrag.attr('style', 'display:block');
  123 + common.attr('style', 'display:none');
  124 + }
  125 + }
  126 +
  127 + // file selection
  128 + function FileSelectHandler(e) {
  129 + var files = e.target.files || e.dataTransfer.files,
  130 + parent = $(e.target.offsetParent);
  131 +
  132 + // process all File objects
  133 + for (var i = 0, f; f = files[i]; i++) {
  134 + parent.find('.filedrag').html(f.name);
  135 + }
  136 + }
  137 + </script>
  138 +{% endblock %}
0 139 \ No newline at end of file
... ...
subjects/templates/subjects/subject_card.html
... ... @@ -31,6 +31,7 @@
31 31 <li><a href="{% url 'subjects:update' subject.slug %}"><i class="fa fa-pencil fa-fw" aria-hidden="true"></i>{% trans 'Edit' %}</a></li>
32 32 <li><a href="{% url 'groups:index' subject.slug %}"><i class="fa fa-group fa-fw" aria-hidden="true"></i>{% trans 'Groups' %}</a></li>
33 33 <li><a href="{% url 'subjects:backup' subject.slug %}"><i class="fa fa-database fa-fw" aria-hidden="true"></i>{% trans 'Backup' %}</a></li>
  34 + <li><a href="{% url 'subjects:restore' subject.slug %}"><i class="fa fa-recycle fa-fw" aria-hidden="true"></i>{% trans 'Restore' %}</a></li>
34 35 <li><a href="javascript:delete_subject.get('{% url 'subjects:delete' subject.slug %}?view=index','#subject','#modal_subject')"><i class="fa fa-trash fa-fw" aria-hidden="true"></i>&nbsp;{% trans 'Remove' %}</a></li>
35 36 </ul>
36 37 {% endif %}
... ...
subjects/urls.py
... ... @@ -10,7 +10,9 @@ urlpatterns = [
10 10 url(r'^update/(?P<slug>[\w_-]+)/$', views.SubjectUpdateView.as_view(), name='update'),
11 11 url(r'^delete/(?P<slug>[\w_-]+)/$', views.SubjectDeleteView.as_view(), name='delete'),
12 12 url(r'^backup/(?P<slug>[\w_-]+)/$', views.Backup.as_view(), name='backup'),
  13 + url(r'^restore/(?P<slug>[\w_-]+)/$', views.Restore.as_view(), name='restore'),
13 14 url(r'^do_backup/(?P<subject>[\w_-]+)/$', views.realize_backup, name='do_backup'),
  15 + url(r'^do_restore/(?P<subject>[\w_-]+)/$', views.realize_restore, name='do_restore'),
14 16 url(r'^view/(?P<slug>[\w_-]+)/$', views.SubjectDetailView.as_view(), name='view'),
15 17 url(r'^view/(?P<slug>[\w_-]+)/(?P<topic_slug>[\w_-]+)/$', views.SubjectDetailView.as_view(), name='topic_view'),
16 18 url(r'^subscribe/(?P<slug>[\w_-]+)/$', views.SubjectSubscribeView.as_view(), name='subscribe'),
... ...
subjects/views.py
... ... @@ -837,4 +837,40 @@ def realize_backup(request, subject):
837 837 resp['Content-Disposition'] = 'attachment; filename=%s' % zip_filename
838 838 resp['Content-Length'] = s.tell()
839 839  
840   - return resp
841 840 \ No newline at end of file
  841 + return resp
  842 +
  843 +class Restore(LoginRequiredMixin, TemplateView):
  844 + login_url = reverse_lazy("users:login")
  845 + redirect_field_name = 'next'
  846 +
  847 + template_name = 'subjects/restore.html'
  848 + model = Subject
  849 +
  850 + def dispatch(self, request, *args, **kwargs):
  851 + subject = get_object_or_404(Subject, slug = kwargs.get('slug', ''))
  852 +
  853 + if not has_subject_permissions(request.user, subject):
  854 + return redirect(reverse_lazy('subjects:home'))
  855 +
  856 + return super(Restore, self).dispatch(request, *args, **kwargs)
  857 +
  858 + def get_context_data(self, **kwargs):
  859 + context = super(Restore, self).get_context_data(**kwargs)
  860 +
  861 + subject = get_object_or_404(Subject, slug = self.kwargs.get('slug', ''))
  862 +
  863 + context['title'] = _('%s - Restore')%(str(subject))
  864 + context['subject'] = subject
  865 +
  866 + return context
  867 +
  868 +@login_required
  869 +def realize_restore(request, subject):
  870 + zip_file = request.FILES.get('zip_file', None)
  871 +
  872 + if zip_file:
  873 + if zipfile.is_zipfile(zip_file):
  874 + file = zipfile.ZipFile(zip_file)
  875 + print(file.namelist())
  876 +
  877 + return JsonResponse({'message': 'ok'})
842 878 \ No newline at end of file
... ...