Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Open sidebar
MOOC packages by hacklab
django-courses
Commits
c2b9eab5
Commit
c2b9eab5
authored
Jun 25, 2020
by
Virgilio Santos
Browse files
project: add certification app to django-courses
parent
eddff068
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
870 additions
and
0 deletions
+870
-0
courses/certification/__init__.py
courses/certification/__init__.py
+1
-0
courses/certification/admin.py
courses/certification/admin.py
+15
-0
courses/certification/apps.py
courses/certification/apps.py
+5
-0
courses/certification/migrations/0001_initial.py
courses/certification/migrations/0001_initial.py
+101
-0
courses/certification/migrations/0002_certificatedata.py
courses/certification/migrations/0002_certificatedata.py
+30
-0
courses/certification/migrations/__init__.py
courses/certification/migrations/__init__.py
+0
-0
courses/certification/models.py
courses/certification/models.py
+311
-0
courses/certification/serializers.py
courses/certification/serializers.py
+139
-0
courses/certification/signals.py
courses/certification/signals.py
+20
-0
courses/certification/urls.py
courses/certification/urls.py
+16
-0
courses/certification/views.py
courses/certification/views.py
+232
-0
No files found.
courses/certification/__init__.py
0 → 100644
View file @
c2b9eab5
__version__
=
'0.0.1'
courses/certification/admin.py
0 → 100644
View file @
c2b9eab5
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
)
courses/certification/apps.py
0 → 100644
View file @
c2b9eab5
from
django.apps
import
AppConfig
class
CoursesCertificationConfig
(
AppConfig
):
name
=
'courses.certification'
courses/certification/migrations/0001_initial.py
0 → 100644
View file @
c2b9eab5
# 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'
),
),
]
courses/certification/migrations/0002_certificatedata.py
0 → 100644
View file @
c2b9eab5
# 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'
)},
},
),
]
courses/certification/migrations/__init__.py
0 → 100644
View file @
c2b9eab5
courses/certification/models.py
0 → 100644
View file @
c2b9eab5
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
courses/certification/serializers.py
0 → 100644
View file @
c2b9eab5
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
)