Commit 301071ab authored by Virgilio Santos's avatar Virgilio Santos
Browse files

project: add certification app to django-courses

parent bf8518ca
from django.contrib import admin
from .models import (
CourseCertification,
CertificateTemplate,
CertificationProcess,
CertificateData,
Evaluation,
)
admin.site.register(CourseCertification)
admin.site.register(CertificateTemplate)
admin.site.register(CertificationProcess)
admin.site.register(CertificateData)
admin.site.register(Evaluation)
from django.apps import AppConfig
class CoursesCertificationConfig(AppConfig):
name = 'courses.certification'
# Generated by Django 2.1.7 on 2019-03-07 23:22
import courses.utils
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('courses', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='CertificateTemplate',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('role', models.CharField(blank=True, max_length=128, null=True, verbose_name='Role')),
('name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Signature Name')),
('cert_logo', models.ImageField(blank=True, null=True, upload_to=courses.utils.HashName('logo', 'organization_name'), verbose_name='Logo')),
('base_logo', models.ImageField(blank=True, null=True, upload_to=courses.utils.HashName('base_logo', 'organization_name'), verbose_name='Logo')),
('signature', models.ImageField(blank=True, null=True, upload_to=courses.utils.HashName('signature', 'organization_name'), verbose_name='Signature')),
('organization_name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Name')),
('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course', verbose_name='Course')),
],
options={
'verbose_name': 'Certificate Template',
},
),
migrations.CreateModel(
name='CertificationProcess',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('comments', models.CharField(blank=True, max_length=255, null=True, verbose_name='Comments')),
('created_date', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
('evaluation_grade', models.IntegerField(blank=True, null=True, verbose_name='Evaluation grade')),
('approved', models.BooleanField(default=False, verbose_name='Approved')),
('no_show', models.BooleanField(default=False, verbose_name='No show')),
('active', models.BooleanField(default=True, verbose_name='Active')),
],
options={
'verbose_name': 'Certification Process',
},
),
migrations.CreateModel(
name='CourseCertification',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('type', models.CharField(choices=[('receipt', 'Receipt'), ('certificate', 'Certificate')], max_length=127, verbose_name='Certificate Type')),
('created_date', models.DateTimeField(auto_now_add=True, verbose_name='Created')),
('modified_date', models.DateTimeField(auto_now=True, verbose_name='Last modified')),
('is_valid', models.BooleanField(default=False, verbose_name='Certificate is valid')),
('course_workload', models.TextField(blank=True, verbose_name='Workload')),
('course_total_units', models.IntegerField(blank=True, verbose_name='Total units')),
('link_hash', models.CharField(max_length=255, unique=True, verbose_name='Hash')),
('course_student', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='certificate', to='courses.CourseStudent', verbose_name='Enrollment')),
],
options={
'verbose_name': 'Certificate',
},
),
migrations.CreateModel(
name='Evaluation',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('min_grade', models.IntegerField(blank=True, verbose_name='Evaluation grade needed')),
('date', models.DateTimeField(blank=True, verbose_name='Evaluation date')),
('results_date', models.DateTimeField(blank=True, verbose_name='Evaluation results date')),
('instructions', models.CharField(max_length=255, verbose_name='Comments')),
('klass', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='evaluations', to='courses.Class', verbose_name='Class')),
],
options={
'verbose_name': 'Evaluation',
},
),
migrations.AddField(
model_name='certificationprocess',
name='course_certification',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='processes', to='certification.CourseCertification', verbose_name='Certificate'),
),
migrations.AddField(
model_name='certificationprocess',
name='evaluation',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='processes', to='certification.Evaluation', verbose_name='Evaluation'),
),
migrations.AddField(
model_name='certificationprocess',
name='klass',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='processes', to='courses.Class', verbose_name='Class'),
),
migrations.AddField(
model_name='certificationprocess',
name='student',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='processes', to=settings.AUTH_USER_MODEL, verbose_name='Student'),
),
]
# Generated by Django 2.2.5 on 2019-09-20 20:01
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('courses', '0002_workspace'),
('certification', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='CertificateData',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('site_logo', models.ImageField(blank=True, null=True, upload_to='certificates_files', verbose_name='Site Logo')),
('text', models.TextField(default='', verbose_name='Content')),
('type', models.CharField(choices=[('receipt', 'Receipt'), ('certificate', 'Certificate')], max_length=127, verbose_name='Certificate Type')),
('certificate_template', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='certification.CertificateTemplate')),
('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Workspace')),
],
options={
'verbose_name': 'Certificate Data',
'unique_together': {('workspace', 'certificate_template', 'type')},
},
),
]
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from django.urls import reverse_lazy
from courses.models import Course, CourseStudent, Class
from courses.workspaces.models import Workspace
#FIXME: remove has_name dependency. Is this really needed?
from courses.utils import hash_name
class CourseCertification(models.Model):
TYPES = (
('receipt', _('Receipt')),
('certificate', _('Certificate')),
)
type = models.CharField(
_('Certificate Type'),
choices=TYPES,
max_length=127,
)
course_student = models.OneToOneField(
CourseStudent,
models.PROTECT,
verbose_name=_('Enrollment'),
related_name='certificate',
)
created_date = models.DateTimeField(
_('Created'),
auto_now_add=True,
)
modified_date = models.DateTimeField(
_('Last modified'),
auto_now=True,
)
is_valid = models.BooleanField(
_('Certificate is valid'),
default=False,
)
course_workload = models.TextField(
_('Workload'),
blank=True,
)
course_total_units = models.IntegerField(
_('Total units'),
blank=True,
)
link_hash = models.CharField(
_('Hash'),
max_length=255,
unique=True,
)
@property
def student(self):
return self.course_student.user
@property
def course(self):
return self.course_student.course
@property
def get_approved_process(self):
return CertificationProcess.objects.get(course_certification=self.id,
approved=True)
@property
def get_absolute_url(self):
return reverse_lazy('courses_legacy:certificate', args=[self.link_hash])
def save(self, *args, **kwargs):
self.course_workload = self.course_student.course.workload
self.course_total_units = self.course_student.units_done.count()
super(CourseCertification, self).save(*args, **kwargs)
class Meta:
verbose_name = _("Certificate")
def __unicode__(self):
return u'({0}): {1}'.format(self.course_student, self.is_valid)
def __str__(self):
return '({0}): {1}'.format(self.course_student, self.is_valid)
class Evaluation(models.Model):
min_grade = models.IntegerField(
_('Evaluation grade needed'),
blank=True,
)
date = models.DateTimeField(
_('Evaluation date'),
blank=True,
)
results_date = models.DateTimeField(
_('Evaluation results date'),
blank=True,
)
instructions = models.CharField(
_('Comments'),
max_length=255,
)
klass = models.ForeignKey(
Class,
models.CASCADE,
verbose_name=_('Class'),
related_name='evaluations',
)
class Meta:
verbose_name = _('Evaluation')
def __unicode__(self):
return u'({0}): {1}'.format(self.klass, self.instructions)
def __str__(self):
return '({0}): {1}'.format(self.klass, self.instructions)
class CertificationProcess(models.Model):
student = models.ForeignKey(
settings.AUTH_USER_MODEL,
models.CASCADE,
verbose_name=_('Student'),
related_name='processes',
)
course_certification = models.ForeignKey(
CourseCertification,
models.CASCADE,
null=True,
related_name="processes",
verbose_name=_('Certificate'),
)
comments = models.CharField(
_('Comments'),
max_length=255,
null=True,
blank=True,
)
created_date = models.DateTimeField(
_('Created'),
auto_now_add=True,
)
evaluation_grade = models.IntegerField(
_('Evaluation grade'),
blank=True,
null=True,
)
approved = models.BooleanField(
_('Approved'),
default=False,
)
no_show = models.BooleanField(
_('No show'),
default=False,
)
evaluation = models.ForeignKey(
Evaluation,
models.SET_NULL,
verbose_name=_('Evaluation'),
blank=True,
null=True,
related_name='processes',
)
klass = models.ForeignKey(
Class,
models.CASCADE,
verbose_name=_('Class'),
related_name='processes',
)
active = models.BooleanField(
_('Active'),
default=True,
)
@property
def certification_progress(self):
return {
'receipt': self.course_certification is not None,
'grade': self.evaluation_grade is not None,
'approved': self.approved
}
class Meta:
verbose_name = _("Certification Process")
def __unicode__(self):
return u'({0}): {1}'.format(
self.course_certification.course_student.user, self.evaluation)
def __str__(self):
return '({0}): {1}'.format(
self.course_certification.course_student.user, self.evaluation)
class CertificateTemplate(models.Model):
course = models.ForeignKey(
Course,
models.CASCADE,
verbose_name=_('Course'),
)
role = models.CharField(
_('Role'),
max_length=128,
blank=True,
null=True,
)
name = models.CharField(
_('Signature Name'),
blank=True,
max_length=255,
null=True,
)
cert_logo = models.ImageField(
_('Logo'),
null=True,
blank=True,
upload_to=hash_name('logo', 'organization_name'),
)
base_logo = models.ImageField(
_('Logo'),
null=True,
blank=True,
upload_to=hash_name('base_logo', 'organization_name'),
)
signature = models.ImageField(
_('Signature'),
null=True,
blank=True,
upload_to=hash_name('signature', 'organization_name'),
)
organization_name = models.CharField(
_('Name'),
max_length=255,
blank=True,
null=True,
)
class Meta:
verbose_name = _('Certificate Template')
def __unicode__(self):
return '({0})'.format(self.course)
def __str__(self):
return '({0})'.format(self.course)
@property
def cert_logo_url(self):
if self.cert_logo:
return self.cert_logo.url
return ''
@property
def base_logo_url(self):
if self.base_logo:
return self.base_logo.url
return ''
@property
def signature_url(self):
if self.signature:
return self.signature.url
return ''
class CertificateData(models.Model):
site_logo = models.ImageField(_('Site Logo'), null=True, blank=True,
upload_to='certificates_files')
text = models.TextField(_('Content'), default='')
TYPES = (
('receipt', _('Receipt')),
('certificate', _('Certificate')),
)
type = models.CharField(
_('Certificate Type'),
choices=TYPES,
max_length=127,
)
workspace = models.ForeignKey(
Workspace,
models.CASCADE,
)
certificate_template = models.ForeignKey(
CertificateTemplate,
models.CASCADE,
)
class Meta:
verbose_name = _('Certificate Data')
unique_together = ('workspace', 'certificate_template', 'type')
def __str__(self):
return 'Data of {0} ({1})'.format(self.workspace, self.type)
@property
def site_logo_url(self):
if self.site_logo:
return self.site_logo.url
return ''
@property
def contrat(self):
return self.workspace
from rest_framework import serializers, status
from courses.workspaces.serializers import SimpleWorkspaceSerializer
from courses.certification.models import (
CertificateData,
CertificateTemplate,
CertificationProcess,
Evaluation,
CourseCertification,
)
class CertificateTemplateSerializer(serializers.ModelSerializer):
course_name = serializers.SerializerMethodField(read_only=True,)
# course = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = CertificateTemplate
fields = (
'id', 'course', 'course_name', 'organization_name',
'base_logo_url', 'cert_logo_url', 'role', 'name',
'signature_url',
)
def get_course_name(self, obj):
return obj.course.name
class CertificateDataSerializer(serializers.ModelSerializer):
workspace = SimpleWorkspaceSerializer(read_only=True)
certificate_template = CertificateTemplateSerializer()
associate = serializers.SerializerMethodField()
# TODO: Legacy compat field, remove in the future
contract = serializers.SerializerMethodField()
class Meta:
model = CertificateData
fields = (
'id', 'text', 'type', 'site_logo_url',
'certificate_template', 'associate', 'workspace', 'contract',
)
def get_associate(self, obj):
if obj.type == 'receipt':
type = 'certificate'
else:
type = 'receipt'
a = CertificateData.objects.filter(
type=type,
certificate_template__course=obj.certificate_template.course,
workspace=obj.workspace,
)
if len(a) > 0:
return a[0].id
else:
return None
def get_contract(self, obj):
return SimpleWorkspaceSerializer(obj.workspace).data
def update(self, instance, validated_data):
ct = dict(validated_data.pop('certificate_template'))
ct['course'] = ct['course'].id
cts = CertificateTemplateSerializer(instance=instance.certificate_template, data=ct)
cts.is_valid(raise_exception=True)
cts.save()
return super().update(instance, validated_data)
class CertificateImageDataSerializer(serializers.ModelSerializer):
class Meta:
model = CertificateData
fields = ('site_logo',)
class CertificationProcessSerializer(serializers.ModelSerializer):
course_certification = serializers.SlugRelatedField(slug_field="link_hash", read_only=True)
class Meta:
model = CertificationProcess
class BaseEvaluationSerializer(serializers.ModelSerializer):
class Meta:
model = Evaluation
class BaseCertificationProcessSerializer(serializers.ModelSerializer):
evaluation = BaseEvaluationSerializer()
class Meta:
model = CertificationProcess
class BaseCourseCertificationSerializer(serializers.ModelSerializer):
class Meta:
model = CourseCertification
class CourseCertificationSerializer(serializers.ModelSerializer):
processes = BaseCertificationProcessSerializer(many=True, read_only=True)
approved = BaseCertificationProcessSerializer(source='get_approved_process', read_only=True)
course = serializers.SerializerMethodField()
url = serializers.ReadOnlyField(source='get_absolute_url')
class Meta:
model = CourseCertification
fields = ('link_hash', 'created_date', 'is_valid', 'processes', 'type',
'approved', 'course', 'url')
@staticmethod
def get_course(obj):
return obj.course.id
class EvaluationSerializer(serializers.ModelSerializer):
processes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)