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
3cbf80bd
Commit
3cbf80bd
authored
Jul 14, 2020
by
Bruno Martin
Browse files
Merge branch 'develop'
parents
5d7f8d82
e506169c
Changes
11
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
159 additions
and
175 deletions
+159
-175
courses/certification/admin.py
courses/certification/admin.py
+0
-2
courses/certification/apps.py
courses/certification/apps.py
+3
-0
courses/certification/migrations/0003_auto_20200630_2323.py
courses/certification/migrations/0003_auto_20200630_2323.py
+19
-0
courses/certification/migrations/0004_auto_20200703_2023.py
courses/certification/migrations/0004_auto_20200703_2023.py
+42
-0
courses/certification/models.py
courses/certification/models.py
+26
-34
courses/certification/serializers.py
courses/certification/serializers.py
+26
-61
courses/certification/signals.py
courses/certification/signals.py
+17
-8
courses/certification/views.py
courses/certification/views.py
+21
-66
courses/classes/serializers.py
courses/classes/serializers.py
+2
-1
courses/models.py
courses/models.py
+1
-1
courses/reports/serializers.py
courses/reports/serializers.py
+2
-2
No files found.
courses/certification/admin.py
View file @
3cbf80bd
...
...
@@ -4,12 +4,10 @@ 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
View file @
3cbf80bd
...
...
@@ -3,3 +3,6 @@ from django.apps import AppConfig
class
CoursesCertificationConfig
(
AppConfig
):
name
=
'courses.certification'
def
ready
(
self
):
import
courses.certification.signals
\ No newline at end of file
courses/certification/migrations/0003_auto_20200630_2323.py
0 → 100644
View file @
3cbf80bd
# Generated by Django 2.2.13 on 2020-07-01 02:23
from
django.db
import
migrations
,
models
import
django.db.models.deletion
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'certification'
,
'0002_certificatedata'
),
]
operations
=
[
migrations
.
AlterField
(
model_name
=
'certificatedata'
,
name
=
'workspace'
,
field
=
models
.
ForeignKey
(
null
=
True
,
on_delete
=
django
.
db
.
models
.
deletion
.
CASCADE
,
to
=
'courses.Workspace'
),
),
]
courses/certification/migrations/0004_auto_20200703_2023.py
0 → 100644
View file @
3cbf80bd
# Generated by Django 2.2.13 on 2020-07-03 23:23
from
django.db
import
migrations
,
models
import
django.db.models.deletion
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'courses'
,
'0009_auto_20200609_1453'
),
(
'certification'
,
'0003_auto_20200630_2323'
),
]
operations
=
[
migrations
.
AddField
(
model_name
=
'certificatetemplate'
,
name
=
'document_type'
,
field
=
models
.
CharField
(
choices
=
[(
'receipt'
,
'Receipt'
),
(
'certificate'
,
'Certificate'
)],
default
=
'receipt'
,
max_length
=
127
,
verbose_name
=
'Certificate Type'
),
),
migrations
.
AddField
(
model_name
=
'certificatetemplate'
,
name
=
'site_logo'
,
field
=
models
.
ImageField
(
blank
=
True
,
null
=
True
,
upload_to
=
'certificates_files'
,
verbose_name
=
'Site Logo'
),
),
migrations
.
AddField
(
model_name
=
'certificatetemplate'
,
name
=
'text'
,
field
=
models
.
TextField
(
default
=
''
,
verbose_name
=
'Content'
),
),
migrations
.
AddField
(
model_name
=
'certificatetemplate'
,
name
=
'workspace'
,
field
=
models
.
ForeignKey
(
null
=
True
,
on_delete
=
django
.
db
.
models
.
deletion
.
CASCADE
,
to
=
'courses.Workspace'
),
),
migrations
.
AlterUniqueTogether
(
name
=
'certificatetemplate'
,
unique_together
=
{(
'workspace'
,
'course'
,
'document_type'
)},
),
migrations
.
DeleteModel
(
name
=
'CertificateData'
,
),
]
courses/certification/models.py
View file @
3cbf80bd
...
...
@@ -197,6 +197,10 @@ class CertificationProcess(models.Model):
class
CertificateTemplate
(
models
.
Model
):
TYPES
=
(
(
'receipt'
,
_
(
'Receipt'
)),
(
'certificate'
,
_
(
'Certificate'
)),
)
course
=
models
.
ForeignKey
(
Course
,
...
...
@@ -233,15 +237,36 @@ class CertificateTemplate(models.Model):
blank
=
True
,
upload_to
=
hash_name
(
'signature'
,
'organization_name'
),
)
site_logo
=
models
.
ImageField
(
_
(
'Site Logo'
),
null
=
True
,
blank
=
True
,
upload_to
=
'certificates_files'
)
organization_name
=
models
.
CharField
(
_
(
'Name'
),
max_length
=
255
,
blank
=
True
,
null
=
True
,
)
text
=
models
.
TextField
(
_
(
'Content'
),
default
=
''
)
document_type
=
models
.
CharField
(
_
(
'Certificate Type'
),
choices
=
TYPES
,
max_length
=
127
,
default
=
TYPES
[
0
][
0
]
)
workspace
=
models
.
ForeignKey
(
Workspace
,
models
.
CASCADE
,
null
=
True
)
class
Meta
:
verbose_name
=
_
(
'Certificate Template'
)
unique_together
=
(
'workspace'
,
'course'
,
'document_type'
)
def
__unicode__
(
self
):
return
'({0})'
.
format
(
self
.
course
)
...
...
@@ -267,39 +292,6 @@ class CertificateTemplate(models.Model):
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
:
...
...
@@ -307,5 +299,5 @@ class CertificateData(models.Model):
return
''
@
property
def
contrat
(
self
):
def
contra
c
t
(
self
):
return
self
.
workspace
courses/certification/serializers.py
View file @
3cbf80bd
...
...
@@ -3,7 +3,6 @@ from rest_framework import serializers, status
from
courses.workspaces.serializers
import
SimpleWorkspaceSerializer
from
courses.certification.models
import
(
CertificateData
,
CertificateTemplate
,
CertificationProcess
,
Evaluation
,
...
...
@@ -12,68 +11,48 @@ from courses.certification.models import (
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
()
course_name
=
serializers
.
SerializerMethodField
(
read_only
=
True
,)
# 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'
,
)
model
=
CertificateTemplate
fields
=
(
'id'
,
'text'
,
'base_logo_url'
,
'cert_logo_url'
,
'role'
,
'name'
,
'course_name'
,
'contract'
,
'associate'
,
'workspace'
,
'document_type'
,
'organization_name'
,
'site_logo_url'
,
'signature_url'
,
'course'
,)
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
:
filters
=
{
'document_type'
:
'certificate'
if
obj
.
document_type
==
'receipt'
else
'receipt'
,
'course'
:
obj
.
course
,
'workspace'
:
obj
.
workspace
}
if
CertificateTemplate
.
objects
.
filter
(
**
filters
).
exists
():
return
CertificateTemplate
.
objects
.
filter
(
**
filters
).
first
().
id
return
None
def
get_contract
(
self
,
obj
):
if
obj
.
workspace
:
return
SimpleWorkspaceSerializer
(
obj
.
workspace
).
data
return
None
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
)
def
get_course_name
(
self
,
obj
):
return
obj
.
course
.
name
class
CertificateImageDataSerializer
(
serializers
.
ModelSerializer
):
class
CertificateTemplateCreateSerializer
(
serializers
.
ModelSerializer
):
class
Meta
:
model
=
CertificateTemplate
fields
=
(
'course'
,
'document_type'
,
'workspace'
)
class
CertificateTemplateImageSerializer
(
serializers
.
ModelSerializer
):
class
Meta
:
model
=
Certificate
D
at
a
fields
=
(
'site_logo'
,
)
model
=
Certificate
Templ
at
e
fields
=
(
'base_logo'
,
'cert_logo'
,
'signature'
,
'site_logo'
)
class
CertificationProcessSerializer
(
serializers
.
ModelSerializer
):
...
...
@@ -123,17 +102,3 @@ class EvaluationSerializer(serializers.ModelSerializer):
class
Meta
:
model
=
Evaluation
class
CertificateTemplateSerializer
(
serializers
.
ModelSerializer
):
class
Meta
:
model
=
CertificateTemplate
fields
=
(
'id'
,
'course'
,
'organization_name'
,
'base_logo_url'
,
'cert_logo_url'
,
'role'
,
'name'
,
'signature_url'
,
)
class
CertificateTemplateImageSerializer
(
serializers
.
ModelSerializer
):
class
Meta
:
model
=
CertificateTemplate
fields
=
(
'base_logo'
,
'cert_logo'
,
'signature'
,
)
courses/certification/signals.py
View file @
3cbf80bd
from
django.db.models.signals
import
post_save
from
django.dispatch
import
receiver
from
base64
import
urlsafe_b64encode
as
ub64
from
hashlib
import
sha1
from
time
import
time
from
courses.models
import
CourseStudent
from
courses.certification.models
import
CourseCertification
from
courses.models
import
Course
,
CourseStudent
from
courses.certification.models
import
CourseCertification
,
CertificateTemplate
@
receiver
(
post_save
,
sender
=
CourseStudent
)
def
course_student_created_or_updated
(
sender
,
created
,
instance
,
**
kwargs
):
def
course_student_created_or_updated
(
sender
,
created
,
instance
:
CourseStudent
,
**
kwargs
):
if
created
:
link_hash
=
ub64
(
sha1
(
str
(
time
())
+
instance
.
user
.
last_name
.
encode
(
'utf-8'
)).
digest
()[
0
:
6
])
link_hash
=
ub64
(
sha1
(
(
str
(
time
())
+
instance
.
user
.
last_name
)
.
encode
(
'utf-8'
)).
digest
()[
0
:
6
])
receipt
=
CourseCertification
(
course_student
=
instance
,
course
=
instance
.
course
,
type
=
CourseCertification
.
TYPES
[
0
][
0
],
is_valid
=
True
,
link_hash
=
link_hash
.
decode
())
receipt
.
save
()
@
receiver
(
post_save
,
sender
=
Course
)
def
course_created
(
sender
,
created
,
instance
,
**
kwargs
):
if
created
:
for
type_
in
CertificateTemplate
.
TYPES
:
ct
=
CertificateTemplate
(
course
=
instance
,
document_type
=
type_
[
0
])
ct
.
save
()
courses/certification/views.py
View file @
3cbf80bd
...
...
@@ -9,16 +9,14 @@ from django.http import Http404
from
courses.permissions
import
IsProfessorCoordinatorOrAdminPermissionOrReadOnly
from
courses.certification.serializers
import
(
CertificateDataSerializer
,
CertificateImageDataSerializer
,
CertificationProcessSerializer
,
CourseCertificationSerializer
,
CertificateTemplateSerializer
,
CertificateTemplateCreateSerializer
,
CertificateTemplateImageSerializer
,
)
from
courses.certification.models
import
(
CertificateData
,
CertificationProcess
,
CourseCertification
,
CertificateTemplate
,
...
...
@@ -30,12 +28,12 @@ from courses.workspaces.models import Workspace
class
CertificateDataMixin
(
viewsets
.
ModelViewSet
):
def
get_queryset
(
self
):
queryset
=
Certificate
D
at
a
.
objects
.
all
()
queryset
=
Certificate
Templ
at
e
.
objects
.
all
()
course
=
self
.
request
.
query_params
.
get
(
'course'
,
None
)
if
course
:
queryset
=
queryset
.
filter
(
certificate_template__
course
=
course
)
queryset
=
queryset
.
filter
(
course
=
course
)
workspace
=
self
.
request
.
query_params
.
get
(
'
contr
ac
t
'
,
None
)
workspace
=
self
.
request
.
query_params
.
get
(
'
worksp
ac
e
'
,
None
)
if
workspace
:
queryset
=
queryset
.
filter
(
workspace
=
workspace
)
...
...
@@ -43,41 +41,13 @@ class CertificateDataMixin(viewsets.ModelViewSet):
class
CertificateDataViewSet
(
CertificateDataMixin
,
viewsets
.
ModelViewSet
):
model
=
CertificateData
serializer_class
=
CertificateDataSerializer
model
=
CertificateTemplate
permission_classes
=
(
IsProfessorCoordinatorOrAdminPermissionOrReadOnly
,
)
def
create
(
self
,
request
,
*
args
,
**
kwargs
):
generate
=
request
.
data
.
get
(
'generate'
,
False
)
if
generate
:
data
=
request
.
data
current
=
CertificateData
.
objects
.
filter
(
certificate_template__course__id
=
data
.
get
(
'course'
,
None
),
workspace__id
=
data
.
get
(
'workspace'
,
None
))
if
len
(
current
)
>=
2
:
return
Response
({
'error'
:
'Os templates já existem'
},
status
=
status
.
HTTP_400_BAD_REQUEST
)
else
:
course
=
Course
.
objects
.
get
(
pk
=
data
.
get
(
'course'
,
None
))
workspace
=
Workspace
.
objects
.
get
(
pk
=
data
.
get
(
'contract'
,
None
))
if
len
(
current
)
>
0
:
ct
=
CertificateTemplate
(
course
=
course
)
ct
.
save
()
t
=
CertificateData
.
TYPES
[
0
]
if
'receipt'
==
current
[
0
].
type
:
t
=
CertificateData
.
TYPES
[
1
]
cd
=
CertificateData
(
type
=
t
[
0
],
workspace
=
workspace
,
certificate_template
=
ct
)
cd
.
save
()
return
Response
({
'message'
:
'Os templates foram criados com sucesso'
})
else
:
for
t
in
CertificateData
.
TYPES
:
ct
=
CertificateTemplate
(
course
=
course
)
ct
.
save
()
cd
=
CertificateData
(
type
=
t
[
0
],
workspace
=
workspace
,
certificate_template
=
ct
)
cd
.
save
()
return
Response
({
'message'
:
'Os templates foram criados com sucesso'
})
else
:
super
().
create
(
request
,
*
args
,
**
kwargs
)
def
get_serializer_class
(
self
):
if
self
.
action
==
'create'
:
return
CertificateTemplateCreateSerializer
return
CertificateTemplateSerializer
@
action
(
detail
=
True
,
methods
=
[
'get'
,
'post'
])
def
images
(
self
,
request
,
pk
=
None
):
...
...
@@ -85,37 +55,22 @@ class CertificateDataViewSet(CertificateDataMixin, viewsets.ModelViewSet):
errors
=
[]
clear_logos
=
(
(
'cert_logo'
,
request
.
data
.
get
(
'cert_logo_clear'
,
None
)),
(
'base_logo'
,
request
.
data
.
get
(
'base_logo_clear'
,
None
)),
(
'signature'
,
request
.
data
.
get
(
'signature_clear'
,
None
)),
(
'
cert
_logo'
,
request
.
data
.
get
(
'
cert
_logo_clear'
,
None
)),
(
'
site
_logo'
,
request
.
data
.
get
(
'
site
_logo_clear'
,
None
)),
)
for
cl
in
clear_logos
:
if
(
cl
[
1
]):
setattr
(
obj
.
certificate_template
,
cl
[
0
],
None
)
ct_serializer
=
CertificateTemplateImageSerializer
(
obj
.
certificate_template
,
request
.
FILES
)
if
ct_serializer
.
is_valid
():
ct_serializer
.
save
()
else
:
errors
+=
ct_serializer
.
errors
cl
=
(
'site_logo'
,
request
.
data
.
get
(
'site_logo_clear'
,
None
))
if
(
cl
[
1
]):
setattr
(
obj
,
cl
[
0
],
None
)
serializer
=
CertificateImageDataSerializer
(
obj
,
request
.
FILES
)
serializer
=
CertificateTemplateImageSerializer
(
obj
,
request
.
FILES
)
if
serializer
.
is_valid
():
serializer
.
save
()
return
Response
(
CertificateTemplateSerializer
(
obj
).
data
,
status
=
status
.
HTTP_200_OK
)
else
:
errors
+=
serializer
.
errors
if
len
(
errors
)
>
0
:
return
Response
(
serializer
.
errors
,
status
=
status
.
HTTP_400_BAD_REQUEST
)
else
:
s
=
CertificateDataSerializer
(
obj
)
return
Response
(
s
.
data
,
status
=
status
.
HTTP_200_OK
)
return
Response
(
serializer
.
errors
,
status
=
status
.
HTTP_400_BAD_REQUEST
)
class
CertificationProcessViewSet
(
viewsets
.
ModelViewSet
):
model
=
CertificationProcess
...
...
@@ -173,8 +128,8 @@ class CertificateTemplateImageViewSet(viewsets.ModelViewSet):
def
post
(
self
,
request
,
**
kwargs
):
certificate_template
=
self
.
get_object
()
serializer
=
CertificateTemplateImageSerializer
(
certificate_template
,
request
.
FILES
)
serializer
=
CertificateTemplateImageSerializer
(
certificate_template
,
request
.
FILES
)
if
serializer
.
is_valid
():
serializer
.
save
()
...
...
@@ -199,9 +154,9 @@ class CourseCertificationDetailView(DetailView):
raise
Http404
if
certificate
:
context
[
'cert_template'
]
=
Certificate
D
at
a
.
objects
.
get
(
certificate_template__
course
=
certificate
.
course_student
.
course
,
type
=
certificate
.
type
,
context
[
'cert_template'
]
=
Certificate
Templ
at
e
.
objects
.
get
(
course
=
certificate
.
course_student
.
course
,
document_
type
=
certificate
.
document_
type
,
workspace
=
workspace
)
# Interpolate data into text string
...
...
courses/classes/serializers.py
View file @
3cbf80bd
...
...
@@ -34,7 +34,8 @@ class CourseStudentClassSerializer(serializers.ModelSerializer):
class
ClassSerializer
(
WorkspaceBaseSerializerMixin
,
serializers
.
ModelSerializer
):
students_details
=
CourseStudentClassSerializer
(
source
=
'get_students'
,
many
=
True
,
read_only
=
True
)
enrolled_students
=
CourseStudentClassSerializer
(
source
=
'get_students'
,
many
=
True
,
read_only
=
True
)
students_details
=
SimpleUserSerializer
(
source
=
'students'
,
many
=
True
,
read_only
=
True
)
processes
=
CertificationProcessSerializer
(
read_only
=
True
,
many
=
True
)
evaluations
=
EvaluationSerializer
(
read_only
=
True
,
many
=
True
)
course
=
BasicCourseSerializer
(
read_only
=
True
)
...
...
courses/models.py
View file @
3cbf80bd
...
...
@@ -352,7 +352,7 @@ class CourseStudent(models.Model):
def
can_emmit_receipt
(
self
):
if
not
self
.
get_current_class
().
user_can_certificate
and
not
self
.
course_finished
:
return
False
if
self
.
get_current_class
().
user_can_certificate_even_without_progress
and
self
.
certificate
.
type
==
'certificate'
:
if
self
.
get_current_class
().
user_can_certificate_even_without_progress
and
self
.
certificate
.
document_
type
==
'certificate'
:
return
True
return
self
.
course_finished
...
...
courses/reports/serializers.py
View file @
3cbf80bd
...
...
@@ -43,7 +43,7 @@ class UserInDetailSerializer(serializers.ModelSerializer):
'course_finished'
:
x
.
can_emmit_receipt
(),
'course_name'
:
x
.
course
.
name
,
# FIXME
# 'has_certificate': x.certificate.type == 'certificate',
# 'has_certificate': x.certificate.
document_
type == 'certificate',
'has_certificate'
:
False
,
'class_name'
:
x
.
get_current_class
().
name
}
for
x
in
obj
.
coursestudent_set
.
all
()]
...
...
@@ -110,7 +110,7 @@ class UsersByClassSerializer(serializers.Serializer):
return
obj
.
user
.
last_login
# def get_has_certificate(self, obj):
# return obj.certificate.type == 'certificate'
# return obj.certificate.
document_
type == 'certificate'
def
get_percent_progress_by_lesson
(
self
,
obj
):
return
obj
.
percent_progress_by_lesson
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment