449 lines
19 KiB
Python
449 lines
19 KiB
Python
from django.http import HttpResponse
|
|
from django.http import HttpResponseRedirect
|
|
from django.shortcuts import render
|
|
from django.views.generic import View
|
|
from django.views.generic import TemplateView
|
|
from django.template.response import TemplateResponse
|
|
from django.utils.decorators import method_decorator
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
from django.http import Http404
|
|
from django.urls import reverse
|
|
from django.utils.html import escape
|
|
from django.core.paginator import Paginator
|
|
|
|
from application import views as ctrl
|
|
from application import forms
|
|
|
|
#
|
|
# Trivial views
|
|
#
|
|
|
|
class SoonView(TemplateView):
|
|
template_name = "soon.html"
|
|
|
|
class LegalTosView(TemplateView):
|
|
template_name = "legal/tos.html"
|
|
|
|
class LegalPrivView(TemplateView):
|
|
template_name = "legal/privacy.html"
|
|
|
|
#
|
|
# ABSTRACT VIEWS
|
|
#
|
|
|
|
class TemplateViewLoggedIn(TemplateView, LoginRequiredMixin):
|
|
@method_decorator(login_required)
|
|
def dispatch(self, *args, **kwargs):
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
class UserPartEditFormView(TemplateViewLoggedIn):
|
|
''' Edits OneToOne fields - such as profiles '''
|
|
form = None
|
|
pagetitle = '??? Edit'
|
|
template_name = "forms/form.html"
|
|
on_success = 'index'
|
|
on_success_args = []
|
|
on_success_kwargs = {}
|
|
add_ip = False
|
|
add_user = False
|
|
'''def get_obj(self, bl, pk, ppk): pass'''
|
|
def get_redirection(self, user, model):
|
|
return reverse(
|
|
self.on_success,
|
|
None,
|
|
self.on_success_args,
|
|
self.on_success_kwargs
|
|
)
|
|
def build_form(self, bl=None, req_data=None, pk='0', ppk='0'):
|
|
obj = self.get_obj(bl,pk,ppk)
|
|
if self.add_ip:
|
|
obj.ip = bl._ip
|
|
if self.add_user:
|
|
obj.user = bl.user
|
|
return self.form(req_data,instance=obj)
|
|
def get(self,request,pk='0', ppk='0'):
|
|
rd = request.GET
|
|
if len(rd) == 0:
|
|
rd = None
|
|
return render(request,self.template_name,{
|
|
'title': self.pagetitle,
|
|
'form': self.build_form(ctrl.BusinessLogic(request), rd, pk, ppk),
|
|
})
|
|
def post(self,request,pk='0', ppk='0'):
|
|
bl = ctrl.BusinessLogic(request)
|
|
form = self.build_form(bl,request.POST, pk, ppk)
|
|
err = ''
|
|
saved = False
|
|
try:
|
|
if form.is_valid():
|
|
form.save()
|
|
saved = True
|
|
return HttpResponseRedirect(self.get_redirection(bl.user, form.instance))
|
|
except Exception as e:
|
|
err = escape(str(e)).strip().replace('\n','\n<br>\n')
|
|
return render(request, self.template_name,{
|
|
'title': self.pagetitle,
|
|
'form': form,
|
|
'err': err,
|
|
})
|
|
|
|
class CrudListView(TemplateViewLoggedIn):
|
|
''' Displays ForeingKey fields - such as lists '''
|
|
pagetitle = '???'
|
|
template_name = "forms/crudlist.html"
|
|
'''
|
|
pagetitle = '???'
|
|
template_name = "forms/crudlist.html"
|
|
item_template = None
|
|
edit_url_label = None
|
|
delete_url_label = None
|
|
def get_list_items(self, bl, ppk): pass
|
|
'''
|
|
def get(self,request,ppk='0'):
|
|
items = self.get_list_items(ctrl.BusinessLogic(request), ppk)
|
|
ll = ['0']
|
|
if ppk!='0': ll=[ppk,'0']
|
|
return render(request,self.template_name,{
|
|
'title': self.pagetitle,
|
|
'items': items,
|
|
'current_pk': ppk,
|
|
'item_template': self.item_template,
|
|
'addlink': reverse(self.edit_url_label,None,ll),
|
|
'delete_url_label': self.delete_url_label,
|
|
'edit_url_label': self.edit_url_label,
|
|
})
|
|
|
|
class CrudDeleteView(TemplateViewLoggedIn):
|
|
''' Deletes ForeingKey fields - such as list items '''
|
|
pagetitle = '???'
|
|
'''
|
|
pagetitle = '???'
|
|
def get_redirection(self, user, model): pass
|
|
def get_model(self, bl, pk): pass
|
|
'''
|
|
def get(self,request,pk='0'):
|
|
bl = ctrl.BusinessLogic(request)
|
|
redir = self.get_redirection(bl.user, None)
|
|
if pk!='0':
|
|
model = self.get_model(bl,pk)
|
|
redir = self.get_redirection(bl.user, model)
|
|
model.delete()
|
|
return HttpResponseRedirect(redir)
|
|
|
|
class CrudEditView(UserPartEditFormView):
|
|
''' Edits/adds ForeingKey fields - such as list items '''
|
|
pagetitle = '???'
|
|
template_name = "forms/form.html"
|
|
on_success = None
|
|
add_ip = True
|
|
add_user = True
|
|
manager = None
|
|
def get_obj(self, bl, pk, ppk):
|
|
if pk=='0':
|
|
obj = self.manager()
|
|
if ppk!='0':
|
|
self.insert_parent(obj, bl, ppk)
|
|
return obj
|
|
else: return self.manager.objects.get(pk=pk, user__pk=bl.user.pk)
|
|
|
|
#
|
|
# CONCRETE VIEWS
|
|
#
|
|
|
|
class HomeView(TemplateView):
|
|
template_name = "index.html"
|
|
def get(self,request):
|
|
bl = ctrl.BusinessLogic(request)
|
|
d = {}
|
|
d['bl'] = bl
|
|
d['loginform'] = False
|
|
if not bl.logged_in:
|
|
d['loginform'] = forms.AuthenticationForm()
|
|
else:
|
|
d['exercises'] = ctrl.models.Exercise.objects.filter(user__pk=bl.user.pk).order_by('-modified')
|
|
return render(
|
|
request,
|
|
self.template_name,
|
|
d
|
|
)
|
|
|
|
class SettingsView(TemplateViewLoggedIn):
|
|
template_name = "settings/index.html"
|
|
|
|
import io
|
|
import uuid
|
|
import zipfile
|
|
|
|
class ExerciseAddView(TemplateViewLoggedIn):
|
|
template_name = "exercise/add.html"
|
|
def post(self, request):
|
|
bl = ctrl.BusinessLogic(request)
|
|
form = forms.ExeciseAddForm(request.POST, request.FILES)
|
|
if form.is_valid():
|
|
f = form.cleaned_data
|
|
name = f['exc_name_prv'] if f['exc_name']=='provided' else str(f['f'])
|
|
if name is None or len(name)==0:
|
|
name = uuid.uuid4()
|
|
exc = ctrl.models.Exercise()
|
|
exc.title = name[:255]
|
|
exc.user = bl.user
|
|
exc.ip = bl._ip
|
|
exc.save()
|
|
if f['org']!='e':
|
|
fileIsName = f['org']=='f'
|
|
posPad = 0 if fileIsName else -1
|
|
zip = None
|
|
try:
|
|
zip = zipfile.ZipFile(request.FILES['f'])
|
|
except zipfile.BadZipfile:
|
|
return HttpResponse('Bad Zip file')
|
|
for fileinfo in zip.infolist():
|
|
if fileinfo.filename.endswith('/'):
|
|
continue
|
|
filepath = fileinfo.filename
|
|
if (
|
|
filepath.endswith('.c')
|
|
or
|
|
filepath.endswith('.cc')
|
|
or
|
|
filepath.endswith('.cpp')
|
|
or
|
|
filepath.endswith('.cxx')
|
|
or
|
|
filepath.endswith('.java')
|
|
):
|
|
filename = filepath.split('/')[filepath.count('/')+posPad]
|
|
ext = filepath.split('.')[-1]
|
|
code = zip.open(fileinfo,'r').read().decode('utf-8','ignore')
|
|
sub = ctrl.models.Submission()
|
|
sub.exercise = exc
|
|
sub.student = filename
|
|
sub.filename = str(uuid.uuid4())+'.'+ext
|
|
sub.code = code
|
|
sub.user = bl.user
|
|
sub.ip = bl._ip
|
|
sub.save()
|
|
return HttpResponseRedirect(reverse('index'))
|
|
else:
|
|
return HttpResponse('Invalid form')
|
|
|
|
class ExerciseView(CrudListView):
|
|
template_name = "exercise/view.html"
|
|
pagetitle = 'Submissions'
|
|
item_template = 'forms/listitem_exercise_submission.html'
|
|
edit_url_label = 'submission_edt'
|
|
delete_url_label = 'submission_del'
|
|
def get_list_items(self, bl, ppk): return ctrl.models.Submission.objects.filter(exercise__pk=ppk, user__pk=bl.user.pk).order_by('student')
|
|
|
|
class ExerciseDelView(CrudDeleteView):
|
|
def get_model(self, bl, pk): return ctrl.models.Exercise.objects.get(pk=pk, user__pk=bl.user.pk)
|
|
def get_redirection(self, user, model): return reverse('index')
|
|
|
|
class ExerciseEdtView(CrudEditView):
|
|
pagetitle = 'Exercise'
|
|
on_success = 'index'
|
|
form = forms.ExerciseForm
|
|
manager = ctrl.models.Exercise
|
|
|
|
class SubmissionDelView(CrudDeleteView):
|
|
def get_model(self, bl, pk): return ctrl.models.Submission.objects.get(pk=pk, user__pk=bl.user.pk)
|
|
def get_redirection(self, user, model):
|
|
if model is None: return reverse('index')
|
|
else: return reverse('exercise', None, [model.exercise.pk])
|
|
|
|
class SubmissionEdtView(CrudEditView):
|
|
pagetitle = 'Submission'
|
|
on_success = 'exercise'
|
|
form = forms.SubmissionForm
|
|
manager = ctrl.models.Submission
|
|
def insert_parent(self, obj, bl, ppk):
|
|
exc = ctrl.models.Exercise.objects.get(user__pk=bl.user.pk, pk=ppk)
|
|
obj.exercise = exc
|
|
def get_redirection(self, user, model):
|
|
self.on_success_args = [model.exercise.pk]
|
|
return super().get_redirection(user,model)
|
|
|
|
import json
|
|
import re
|
|
import graphviz
|
|
|
|
relPlaPCodigo = re.compile(r'''Variaveis;(.*)\n?\|\|>(.*);([0-9. ]+)\n?<\|\|\|\|>(.*);([0-9. ]+)\n?<\|\|Similaridade: ([0-9.]+)[^\)]+\)''')
|
|
|
|
class MetricsExplorer(TemplateViewLoggedIn):
|
|
template_name = "metrics/explorer.html"
|
|
def get(self, request, pk, filedownload=None):
|
|
bl = ctrl.BusinessLogic(request)
|
|
results = ctrl.models.Results.objects.filter(exercise__user__pk=bl.user.pk, exercise__pk=pk)
|
|
has_metrics = results.count()
|
|
mapacalor = None
|
|
graficosvg = None
|
|
plagiarism = None
|
|
plagiarism_structured = None
|
|
plagmap_svg = None
|
|
plagmap_dot = None
|
|
plagmap_pdf = None
|
|
plagmap_png = None
|
|
if has_metrics:
|
|
identidades = json.loads(results.get(label='identidades').data.decode())
|
|
graficosvg = results.get(label='graficosvg').data.decode()
|
|
graficopdf = results.get(label='graficopdf').data
|
|
mapacalor = results.get(label='mapacalor').data.decode()
|
|
simplagio = results.get(label='simplagio').data.decode()
|
|
tabmetricas = results.get(label='tabmetricas').data.decode()
|
|
plagiarism = results.get(label='plagiarism').data.decode()
|
|
plagiarism_structured = json.loads(results.get(label='plagiarism_structured').data.decode())
|
|
plagmap_svg = results.get(label='plagmap_svg').data.decode()
|
|
plagmap_dot = results.get(label='plagmap_dot').data.decode()
|
|
plagmap_pdf = results.get(label='plagmap_pdf').data
|
|
plagmap_png = results.get(label='plagmap_png').data
|
|
|
|
if filedownload is not None:
|
|
if filedownload == 'heatmap':
|
|
res = HttpResponse(mapacalor)
|
|
res.mimetype='application/force-download'
|
|
res['Content-Disposition'] = 'attachment; filename=%s' % 'heatmap.html'
|
|
return res
|
|
if filedownload == 'clusterssvg':
|
|
res = HttpResponse(graficosvg)
|
|
res.mimetype='application/force-download'
|
|
res['Content-Disposition'] = 'attachment; filename=%s' % 'clusters.svg'
|
|
return res
|
|
if filedownload == 'clusterspdf':
|
|
res = HttpResponse(graficopdf)
|
|
res.mimetype='application/force-download'
|
|
res['Content-Disposition'] = 'attachment; filename=%s' % 'clusters.pdf'
|
|
return res
|
|
if filedownload == 'plagiarism':
|
|
res = HttpResponse(json.dumps(plagiarism_structured))
|
|
res.mimetype='application/force-download'
|
|
res['Content-Disposition'] = 'attachment; filename=%s' % 'plagiarism.json'
|
|
return res
|
|
if filedownload == 'plagiarismmapdot':
|
|
res = HttpResponse(plagmap_dot)
|
|
res.mimetype='application/force-download'
|
|
res['Content-Disposition'] = 'attachment; filename=%s' % 'plagiarismmap.gv'
|
|
return res
|
|
if filedownload == 'plagiarismmappdf':
|
|
res = HttpResponse(plagmap_pdf)
|
|
res.mimetype='application/force-download'
|
|
res['Content-Disposition'] = 'attachment; filename=%s' % 'plagiarismmap.pdf'
|
|
return res
|
|
if filedownload == 'plagiarismmappng':
|
|
res = HttpResponse(plagmap_png)
|
|
res.mimetype='application/force-download'
|
|
res['Content-Disposition'] = 'attachment; filename=%s' % 'plagiarismmap.png'
|
|
return res
|
|
if filedownload == 'plagiarismmapsvg':
|
|
res = HttpResponse(plagmap_svg)
|
|
res.mimetype='application/force-download'
|
|
res['Content-Disposition'] = 'attachment; filename=%s' % 'plagiarismmap.svg'
|
|
return res
|
|
else:
|
|
raise Http404
|
|
else:
|
|
if filedownload is not None:
|
|
raise Http404
|
|
return render(request, self.template_name, {
|
|
'pk':pk,
|
|
'has_metrics':has_metrics,
|
|
'heatmap':mapacalor,
|
|
'clusters':graficosvg,
|
|
'plagiarism':plagiarism_structured,
|
|
'plagiarismmap':plagmap_svg,
|
|
# 'plagiarism_pretty':json.dumps(plagiarism_structured, sort_keys=True, indent=4)
|
|
})
|
|
import os
|
|
import subprocess
|
|
|
|
class MetricsExplorerRun(TemplateViewLoggedIn):
|
|
def get(self, request, pk):
|
|
bl = ctrl.BusinessLogic(request)
|
|
submissoes = os.path.join('workspace/submissoes_normalizadorStandalone/in',pk)
|
|
subprocess.run(['rm','-rf','workspace/submissoes'])
|
|
subprocess.run(['rm','-rf','workspace/submissoes_normalizadorStandalone/in'])
|
|
subprocess.run(['mkdir','-p','workspace/submissoes'])
|
|
subprocess.run(['mkdir','-p',submissoes])
|
|
exercise = ctrl.models.Exercise.objects.get(pk=pk, user__pk=bl.user.pk)
|
|
submissions = exercise.submissions.all()
|
|
for sb in submissions:
|
|
if not os.path.exists(os.path.join(submissoes,sb.student)):
|
|
os.makedirs(os.path.join(submissoes,sb.student))
|
|
with open(os.path.join(submissoes,sb.student,sb.filename),'wt') as f:
|
|
f.write(sb.code)
|
|
subprocess.run(['make','-C','workspace'])
|
|
my_env = os.environ.copy()
|
|
my_env["PATH"] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
|
grf = os.path.join('workspace/processado/metricas',pk,'grafico')
|
|
subprocess.run(['inkscape', '--export-plain-svg', grf+'.svg', grf+'.eps'], env=my_env)
|
|
subprocess.run(['inkscape', '--export-pdf', grf+'.pdf', grf+'.eps'], env=my_env)
|
|
ctrl.models.Results.objects.filter(exercise = exercise).delete()
|
|
unsaved = list()
|
|
unsaved.append(ctrl.models.Results(exercise = exercise, label = 'identidades', data = open(os.path.join('workspace/submissoes',pk,'identidades.json'),'rb').read()))
|
|
unsaved.append(ctrl.models.Results(exercise = exercise, label = 'graficosvg', data = open(os.path.join('workspace/processado/metricas',pk,'grafico.svg'),'rb').read()))
|
|
unsaved.append(ctrl.models.Results(exercise = exercise, label = 'graficopdf', data = open(os.path.join('workspace/processado/metricas',pk,'grafico.pdf'),'rb').read()))
|
|
unsaved.append(ctrl.models.Results(exercise = exercise, label = 'mapacalor', data = open(os.path.join('workspace/processado/metricas',pk,'plot.html'),'rb').read()))
|
|
unsaved.append(ctrl.models.Results(exercise = exercise, label = 'tabmetricas', data = open(os.path.join('workspace/processado/metricas',pk,'tabelaParaMapaDeCalor.csv'),'rb').read()))
|
|
unsaved.append(ctrl.models.Results(exercise = exercise, label = 'simplagio', data = open(os.path.join('workspace/processado/plagio',pk,'relatorioplagio.csv'),'rb').read()))
|
|
|
|
identidades = json.loads(unsaved[0].data.decode())
|
|
plagiarism = '\n'.join('\n'.join(unsaved[-1].data.decode().splitlines()[2:]).split('\n\n'))
|
|
plagiarism_structured = relPlaPCodigo.findall(plagiarism)
|
|
for i,p in enumerate(plagiarism_structured):
|
|
plagiarism_structured[i]=[
|
|
float(p[5]),
|
|
'%7.4f%%'%(float(p[5])*100),
|
|
identidades.get(p[1],p[1]),
|
|
identidades.get(p[3],p[3]),
|
|
list([
|
|
['Aluno']+p[0].split(';'),
|
|
[identidades.get(p[1],p[1])]+[float(k) for k in p[2].split(' ')],
|
|
[identidades.get(p[3],p[3])]+[float(k) for k in p[4].split(' ')],
|
|
]),
|
|
list(zip(
|
|
['Aluno']+p[0].split(';'),
|
|
[identidades.get(p[1],p[1])]+[float(k) for k in p[2].split(' ')],
|
|
[identidades.get(p[3],p[3])]+[float(k) for k in p[4].split(' ')],
|
|
)),
|
|
]
|
|
plagiarism_structured.sort()
|
|
plagiarism_structured.reverse()
|
|
unsaved.append(ctrl.models.Results(exercise = exercise, label = 'plagiarism', data = plagiarism.encode()))
|
|
unsaved.append(ctrl.models.Results(exercise = exercise, label = 'plagiarism_structured', data = json.dumps(plagiarism_structured).encode()))
|
|
|
|
plagmap = os.path.join('workspace/processado/metricas',pk,'plagiarismmap')
|
|
dot = graphviz.Graph()
|
|
for plag in plagiarism_structured:
|
|
dot.node(plag[2])
|
|
dot.node(plag[3])
|
|
dot.edge(
|
|
plag[2], # alumni 1
|
|
plag[3], # alumni 2
|
|
plag[1], # similarity text
|
|
color="#"+('00'+hex(int((1-((plag[0]-0.9)*10))*255))[2:])[-2:]*3)
|
|
dot.render(plagmap+'.gv')
|
|
subprocess.run(['inkscape', '--export-plain-svg', plagmap+'.svg', plagmap+'.gv.pdf'])
|
|
subprocess.run(['inkscape', '--export-png', plagmap+'.png', plagmap+'.gv.pdf'])
|
|
|
|
unsaved.append(ctrl.models.Results(exercise = exercise, label = 'plagmap_png', data = open(plagmap+'.png','rb').read()))
|
|
unsaved.append(ctrl.models.Results(exercise = exercise, label = 'plagmap_svg', data = open(plagmap+'.svg','rb').read()))
|
|
unsaved.append(ctrl.models.Results(exercise = exercise, label = 'plagmap_dot', data = open(plagmap+'.gv','rb').read()))
|
|
unsaved.append(ctrl.models.Results(exercise = exercise, label = 'plagmap_pdf', data = open(plagmap+'.gv.pdf','rb').read()))
|
|
|
|
[data.save() for data in unsaved]
|
|
return HttpResponseRedirect(reverse('metrics_explorer',None,[pk]))
|
|
import time
|
|
|
|
class MetricsExplorerRunPoll(TemplateViewLoggedIn):
|
|
def get(self, request, pk, inc='0'):
|
|
time.sleep(.5)
|
|
return HttpResponseRedirect(reverse('metrics_explorer_run_poll',None,[pk,int(inc)+1]))
|
|
|
|
class MetricsExplorerDel(TemplateViewLoggedIn):
|
|
def get(self, request, pk):
|
|
bl = ctrl.BusinessLogic(request)
|
|
exercise = ctrl.models.Exercise.objects.get(pk=pk, user__pk=bl.user.pk)
|
|
results = ctrl.models.Results.objects.filter(exercise__pk=exercise.pk)
|
|
results.delete()
|
|
return HttpResponseRedirect(reverse('metrics_explorer',None,[pk]))
|