Fakultas Ilmu Komputer UI
Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
PMPL
Class Project
DIGIPUS
Commits
8e7e67a9
Commit
8e7e67a9
authored
Nov 01, 2020
by
Selvy Fitriani
Browse files
Merge branch '1706039446/110' into 'master'
[
#110
] Guest Book For Non Registered User See merge request
!112
parents
defcd506
60a1898e
Pipeline
#60185
passed with stages
in 26 minutes and 38 seconds
Changes
10
Pipelines
1
Expand all
Hide whitespace changes
Inline
Side-by-side
app/forms.py
View file @
8e7e67a9
from
django
import
forms
from
django
import
forms
from
app.models
import
Materi
,
Category
,
RatingContributor
from
app.models
import
Materi
,
Category
,
RatingContributor
,
GuestBook
from
authentication.models
import
User
from
authentication.models
import
User
import
datetime
import
datetime
def
year_choices
():
def
year_choices
():
return
[(
r
,
r
)
for
r
in
range
(
2000
,
datetime
.
date
.
today
().
year
+
1
)]
return
[(
r
,
r
)
for
r
in
range
(
2000
,
datetime
.
date
.
today
().
year
+
1
)]
class
UploadMateriForm
(
forms
.
ModelForm
):
class
UploadMateriForm
(
forms
.
ModelForm
):
categories
=
forms
.
ModelMultipleChoiceField
(
queryset
=
Category
.
objects
.
all
(),
widget
=
forms
.
CheckboxSelectMultiple
(
attrs
=
{
'style'
:
'column-count:2'
}),
required
=
True
)
categories
=
forms
.
ModelMultipleChoiceField
(
queryset
=
Category
.
objects
.
all
(
release_year
=
forms
.
TypedChoiceField
(
coerce
=
int
,
choices
=
year_choices
,
initial
=
datetime
.
date
.
today
().
year
)
),
widget
=
forms
.
CheckboxSelectMultiple
(
attrs
=
{
'style'
:
'column-count:2'
}),
required
=
True
)
yt_video_id
=
forms
.
CharField
(
label
=
"Youtube Video Id"
,
\
release_year
=
forms
.
TypedChoiceField
(
help_text
=
"This is not required.<br>
\
coerce
=
int
,
choices
=
year_choices
,
initial
=
datetime
.
date
.
today
().
year
)
yt_video_id
=
forms
.
CharField
(
label
=
"Youtube Video Id"
,
help_text
=
"This is not required.<br>
\
Please insert only Youtube link videos! Take a note for the video id!<br>
\
Please insert only Youtube link videos! Take a note for the video id!<br>
\
Example : https://www.youtube.com/watch?v=DkJ-50GLi2I <br> has video id DkJ-50GLi2I"
,
required
=
False
)
Example : https://www.youtube.com/watch?v=DkJ-50GLi2I <br> has video id DkJ-50GLi2I"
,
required
=
False
)
...
@@ -29,14 +33,13 @@ class UploadMateriForm(forms.ModelForm):
...
@@ -29,14 +33,13 @@ class UploadMateriForm(forms.ModelForm):
field
.
widget
.
attrs
[
"class"
]
=
"form-control"
field
.
widget
.
attrs
[
"class"
]
=
"form-control"
field
.
widget
.
attrs
[
"placeholder"
]
=
field
.
initial
field
.
widget
.
attrs
[
"placeholder"
]
=
field
.
initial
field
.
initial
=
""
field
.
initial
=
""
self
.
fields
[
'categories'
].
widget
.
attrs
.
update
({
'class'
:
"native"
})
self
.
fields
[
'categories'
].
widget
.
attrs
.
update
({
'class'
:
"native"
})
class
SuntingProfilForm
(
forms
.
ModelForm
):
class
SuntingProfilForm
(
forms
.
ModelForm
):
class
Meta
:
class
Meta
:
model
=
User
model
=
User
fields
=
[
"email"
,
"name"
,
"instansi"
,
"nik"
,
"alamat"
,
"nomor_telpon"
,
fields
=
[
"email"
,
"name"
,
"instansi"
,
"nik"
,
"alamat"
,
"nomor_telpon"
,
"profile_picture"
,
"linkedin"
,
"profile_picture"
,
"linkedin"
,
"facebook"
,
"twitter"
,
"instagram"
,
"biography"
,
"facebook"
,
"twitter"
,
"instagram"
,
"biography"
,
"is_subscribing_to_material_comments"
]
"is_subscribing_to_material_comments"
]
...
@@ -50,7 +53,6 @@ class SuntingProfilForm(forms.ModelForm):
...
@@ -50,7 +53,6 @@ class SuntingProfilForm(forms.ModelForm):
if
any
(
self
.
errors
):
if
any
(
self
.
errors
):
key
=
list
(
self
.
errors
)[
0
]
key
=
list
(
self
.
errors
)[
0
]
self
.
fields
[
key
].
widget
.
attrs
[
"autofocus"
]
=
""
self
.
fields
[
key
].
widget
.
attrs
[
"autofocus"
]
=
""
self
.
fields
[
"email"
].
widget
.
attrs
[
"readonly"
]
=
True
self
.
fields
[
"email"
].
widget
.
attrs
[
"readonly"
]
=
True
...
@@ -61,14 +63,43 @@ class RatingContributorForm(forms.ModelForm):
...
@@ -61,14 +63,43 @@ class RatingContributorForm(forms.ModelForm):
fields
=
[
'score'
,
'user'
,
'contributor'
]
fields
=
[
'score'
,
'user'
,
'contributor'
]
SCORE_CHOICE
=
(
SCORE_CHOICE
=
(
(
''
,
'Select score'
),
(
''
,
'Select score'
),
(
'1'
,
'1'
),
# First one is the value of select option and second is the displayed value in option
# First one is the value of select option and second is the displayed value in option
(
'1'
,
'1'
),
(
'2'
,
'2'
),
(
'2'
,
'2'
),
(
'3'
,
'3'
),
(
'3'
,
'3'
),
(
'4'
,
'4'
),
(
'4'
,
'4'
),
(
'5'
,
'5'
),
(
'5'
,
'5'
),
)
)
widgets
=
{
widgets
=
{
'score'
:
forms
.
Select
(
choices
=
SCORE_CHOICE
,
attrs
=
{
'class'
:
'form-control'
,
'id'
:
'form-rating'
},),
'score'
:
forms
.
Select
(
choices
=
SCORE_CHOICE
,
attrs
=
{
'class'
:
'form-control'
,
'id'
:
'form-rating'
},),
'user'
:
forms
.
HiddenInput
(),
'user'
:
forms
.
HiddenInput
(),
'contributor'
:
forms
.
HiddenInput
()
'contributor'
:
forms
.
HiddenInput
()
}
}
class
GuestBookForm
(
forms
.
models
.
ModelForm
):
class
Meta
:
model
=
GuestBook
fields
=
[
'name'
,
'job'
,
'gender'
]
gender_choices
=
(
(
'Male'
,
'Male'
),
(
'Female'
,
'Female'
)
)
widgets
=
{
'name'
:
forms
.
fields
.
TextInput
(
attrs
=
{
'placeholder'
:
'Input your name'
,
'class'
:
'form-control input-lg'
}),
'job'
:
forms
.
fields
.
TextInput
(
attrs
=
{
'placeholder'
:
'Input your job'
,
'class'
:
'form-control input-lg'
}),
'gender'
:
forms
.
fields
.
Select
(
choices
=
gender_choices
,
attrs
=
{
'class'
:
'form-control input-lg'
})
}
error_messages
=
{
'name'
:
{
'required'
:
'Name is required'
},
'job'
:
{
'required'
:
'Job is required'
},
'gender'
:
{
'required'
:
'Gender is required'
}
}
app/migrations/0029_guestbook.py
0 → 100644
View file @
8e7e67a9
# Generated by Django 3.1 on 2020-10-31 18:19
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'app'
,
'0028_adminnotification'
),
]
operations
=
[
migrations
.
CreateModel
(
name
=
'GuestBook'
,
fields
=
[
(
'id'
,
models
.
AutoField
(
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
verbose_name
=
'ID'
)),
(
'name'
,
models
.
TextField
(
default
=
''
)),
(
'job'
,
models
.
TextField
(
default
=
''
)),
(
'gender'
,
models
.
CharField
(
default
=
'Male'
,
max_length
=
6
)),
],
),
]
app/migrations/00
29
_merge_20201101_021
7
.py
→
app/migrations/00
30
_merge_20201101_0
6
21.py
View file @
8e7e67a9
# Generated by Django 3.1 on 2020-10-31
19:17
# Generated by Django 3.1 on 2020-10-31
23:21
from
django.db
import
migrations
from
django.db
import
migrations
...
@@ -6,8 +6,8 @@ from django.db import migrations
...
@@ -6,8 +6,8 @@ from django.db import migrations
class
Migration
(
migrations
.
Migration
):
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
dependencies
=
[
(
'app'
,
'0029_guestbook'
),
(
'app'
,
'0028_notifikasikontributor'
),
(
'app'
,
'0028_notifikasikontributor'
),
(
'app'
,
'0028_adminnotification'
),
]
]
operations
=
[
operations
=
[
...
...
app/models.py
View file @
8e7e67a9
...
@@ -26,9 +26,11 @@ def get_random_color():
...
@@ -26,9 +26,11 @@ def get_random_color():
color
=
"%06x"
%
random
.
randint
(
0
,
0xFFFFFF
)
color
=
"%06x"
%
random
.
randint
(
0
,
0xFFFFFF
)
return
color
return
color
def
current_year
():
def
current_year
():
return
datetime
.
date
.
today
().
year
return
datetime
.
date
.
today
().
year
class
Category
(
models
.
Model
):
class
Category
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
20
)
name
=
models
.
CharField
(
max_length
=
20
)
description
=
models
.
TextField
(
blank
=
False
,
default
=
""
)
description
=
models
.
TextField
(
blank
=
False
,
default
=
""
)
...
@@ -48,7 +50,7 @@ class MateriManager(models.Manager):
...
@@ -48,7 +50,7 @@ class MateriManager(models.Manager):
if
self
.
alive_only
:
if
self
.
alive_only
:
return
SoftDeletionQuerySet
(
self
.
model
).
filter
(
deleted_at
=
None
)
return
SoftDeletionQuerySet
(
self
.
model
).
filter
(
deleted_at
=
None
)
return
SoftDeletionQuerySet
(
self
.
model
)
return
SoftDeletionQuerySet
(
self
.
model
)
def
search
(
self
,
search_text
):
def
search
(
self
,
search_text
):
search_vector
=
None
search_vector
=
None
for
field
,
weight
in
Materi
.
SEARCH_INDEX
:
for
field
,
weight
in
Materi
.
SEARCH_INDEX
:
...
@@ -75,18 +77,20 @@ class SoftDeletionQuerySet(models.query.QuerySet):
...
@@ -75,18 +77,20 @@ class SoftDeletionQuerySet(models.query.QuerySet):
def
delete
(
self
):
def
delete
(
self
):
return
super
(
SoftDeletionQuerySet
,
self
).
update
(
deleted_at
=
timezone
.
now
())
return
super
(
SoftDeletionQuerySet
,
self
).
update
(
deleted_at
=
timezone
.
now
())
class
SoftDeleteModel
(
models
.
Model
):
class
SoftDeleteModel
(
models
.
Model
):
deleted_at
=
models
.
DateTimeField
(
blank
=
True
,
null
=
True
)
deleted_at
=
models
.
DateTimeField
(
blank
=
True
,
null
=
True
)
all_objects
=
MateriManager
(
alive_only
=
False
)
all_objects
=
MateriManager
(
alive_only
=
False
)
class
Meta
:
class
Meta
:
abstract
=
True
abstract
=
True
def
soft_delete
(
self
):
def
soft_delete
(
self
):
self
.
deleted_at
=
timezone
.
now
()
self
.
deleted_at
=
timezone
.
now
()
self
.
save
()
self
.
save
()
class
Materi
(
SoftDeleteModel
):
class
Materi
(
SoftDeleteModel
):
cover
=
models
.
ImageField
()
cover
=
models
.
ImageField
()
content
=
models
.
FileField
()
content
=
models
.
FileField
()
...
@@ -96,8 +100,9 @@ class Materi(SoftDeleteModel):
...
@@ -96,8 +100,9 @@ class Materi(SoftDeleteModel):
publisher
=
models
.
CharField
(
max_length
=
30
,
default
=
"Penerbit"
)
publisher
=
models
.
CharField
(
max_length
=
30
,
default
=
"Penerbit"
)
release_year
=
models
.
IntegerField
(
default
=
current_year
)
release_year
=
models
.
IntegerField
(
default
=
current_year
)
pages
=
models
.
IntegerField
(
default
=
0
)
pages
=
models
.
IntegerField
(
default
=
0
)
descriptions
=
models
.
TextField
(
default
=
"Deskripsi"
)
descriptions
=
models
.
TextField
(
default
=
"Deskripsi"
)
status
=
models
.
CharField
(
max_length
=
30
,
choices
=
VERIFICATION_STATUS
,
default
=
VERIFICATION_STATUS
[
0
][
0
])
status
=
models
.
CharField
(
max_length
=
30
,
choices
=
VERIFICATION_STATUS
,
default
=
VERIFICATION_STATUS
[
0
][
0
])
categories
=
models
.
ManyToManyField
(
Category
)
categories
=
models
.
ManyToManyField
(
Category
)
date_created
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
date_created
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
date_modified
=
models
.
DateTimeField
(
auto_now
=
True
)
date_modified
=
models
.
DateTimeField
(
auto_now
=
True
)
...
@@ -117,7 +122,8 @@ class Materi(SoftDeleteModel):
...
@@ -117,7 +122,8 @@ class Materi(SoftDeleteModel):
def
save
(
self
,
*
args
,
**
kwargs
):
def
save
(
self
,
*
args
,
**
kwargs
):
super
().
save
(
*
args
,
**
kwargs
)
super
().
save
(
*
args
,
**
kwargs
)
search_index
=
{
field
:
weight
for
(
field
,
weight
)
in
Materi
.
SEARCH_INDEX
}
search_index
=
{
field
:
weight
for
(
field
,
weight
)
in
Materi
.
SEARCH_INDEX
}
if
"update_fields"
in
kwargs
:
if
"update_fields"
in
kwargs
:
is_search_index_updated
=
bool
(
is_search_index_updated
=
bool
(
set
(
search_index
.
keys
())
&
set
(
kwargs
[
"update_fields"
])
set
(
search_index
.
keys
())
&
set
(
kwargs
[
"update_fields"
])
...
@@ -127,9 +133,11 @@ class Materi(SoftDeleteModel):
...
@@ -127,9 +133,11 @@ class Materi(SoftDeleteModel):
self
.
_search_vector
=
None
self
.
_search_vector
=
None
for
field
,
weight
in
search_index
.
items
():
for
field
,
weight
in
search_index
.
items
():
if
self
.
_search_vector
is
None
:
if
self
.
_search_vector
is
None
:
self
.
_search_vector
=
search
.
SearchVector
(
field
,
weight
=
weight
)
self
.
_search_vector
=
search
.
SearchVector
(
field
,
weight
=
weight
)
else
:
else
:
self
.
_search_vector
+=
search
.
SearchVector
(
field
,
weight
=
weight
)
self
.
_search_vector
+=
search
.
SearchVector
(
field
,
weight
=
weight
)
self
.
save
(
update_fields
=
[
"_search_vector"
])
self
.
save
(
update_fields
=
[
"_search_vector"
])
...
@@ -154,7 +162,7 @@ class Materi(SoftDeleteModel):
...
@@ -154,7 +162,7 @@ class Materi(SoftDeleteModel):
def
like_count
(
self
):
def
like_count
(
self
):
count
=
Like
.
objects
.
filter
(
materi
=
self
).
count
()
count
=
Like
.
objects
.
filter
(
materi
=
self
).
count
()
return
count
return
count
@
property
@
property
def
comment_count
(
self
):
def
comment_count
(
self
):
count
=
Comment
.
objects
.
filter
(
materi
=
self
).
count
()
count
=
Comment
.
objects
.
filter
(
materi
=
self
).
count
()
...
@@ -181,7 +189,7 @@ class Materi(SoftDeleteModel):
...
@@ -181,7 +189,7 @@ class Materi(SoftDeleteModel):
@
property
@
property
def
hot_score
(
self
):
def
hot_score
(
self
):
view_score
=
math
.
log
(
max
(
self
.
view_count
,
1
),
10
)
view_score
=
math
.
log
(
max
(
self
.
view_count
,
1
),
10
)
time_score
=
self
.
seconds_since_earliest_materi
/
604800
# 1 week
time_score
=
self
.
seconds_since_earliest_materi
/
604800
# 1 week
return
round
(
view_score
+
time_score
,
7
)
return
round
(
view_score
+
time_score
,
7
)
@
property
@
property
...
@@ -197,7 +205,8 @@ class Comment(models.Model):
...
@@ -197,7 +205,8 @@ class Comment(models.Model):
profile
=
models
.
CharField
(
max_length
=
100
,
default
=
get_random_color
)
profile
=
models
.
CharField
(
max_length
=
100
,
default
=
get_random_color
)
comment
=
models
.
CharField
(
max_length
=
240
,
default
=
"comments"
)
comment
=
models
.
CharField
(
max_length
=
240
,
default
=
"comments"
)
materi
=
models
.
ForeignKey
(
Materi
,
models
.
SET_NULL
,
null
=
True
)
materi
=
models
.
ForeignKey
(
Materi
,
models
.
SET_NULL
,
null
=
True
)
user
=
models
.
ForeignKey
(
User
,
on_delete
=
models
.
SET_NULL
,
blank
=
True
,
null
=
True
)
user
=
models
.
ForeignKey
(
User
,
on_delete
=
models
.
SET_NULL
,
blank
=
True
,
null
=
True
)
timestamp
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
timestamp
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
def
__str__
(
self
):
def
__str__
(
self
):
...
@@ -207,18 +216,20 @@ class Comment(models.Model):
...
@@ -207,18 +216,20 @@ class Comment(models.Model):
def
like_count
(
self
):
def
like_count
(
self
):
count
=
LikeComment
.
objects
.
filter
(
comment
=
self
).
count
()
count
=
LikeComment
.
objects
.
filter
(
comment
=
self
).
count
()
return
count
return
count
@
property
@
property
def
dislike_count
(
self
):
def
dislike_count
(
self
):
count
=
DislikeComment
.
objects
.
filter
(
comment
=
self
).
count
()
count
=
DislikeComment
.
objects
.
filter
(
comment
=
self
).
count
()
return
count
return
count
class
Review
(
models
.
Model
):
class
Review
(
models
.
Model
):
username
=
models
.
CharField
(
max_length
=
100
)
username
=
models
.
CharField
(
max_length
=
100
)
profile
=
models
.
CharField
(
max_length
=
100
,
default
=
get_random_color
)
profile
=
models
.
CharField
(
max_length
=
100
,
default
=
get_random_color
)
review
=
models
.
TextField
(
default
=
"review"
)
review
=
models
.
TextField
(
default
=
"review"
)
materi
=
models
.
ForeignKey
(
Materi
,
models
.
SET_NULL
,
null
=
True
)
materi
=
models
.
ForeignKey
(
Materi
,
models
.
SET_NULL
,
null
=
True
)
user
=
models
.
ForeignKey
(
User
,
on_delete
=
models
.
SET_NULL
,
blank
=
True
,
null
=
True
)
user
=
models
.
ForeignKey
(
User
,
on_delete
=
models
.
SET_NULL
,
blank
=
True
,
null
=
True
)
timestamp
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
timestamp
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
def
__str__
(
self
):
def
__str__
(
self
):
...
@@ -247,6 +258,7 @@ class ReqMaterial(models.Model):
...
@@ -247,6 +258,7 @@ class ReqMaterial(models.Model):
title
=
models
.
CharField
(
max_length
=
100
)
title
=
models
.
CharField
(
max_length
=
100
)
timestamp
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
timestamp
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
class
SubmitVisitor
(
models
.
Model
):
class
SubmitVisitor
(
models
.
Model
):
user_id
=
models
.
CharField
(
max_length
=
50
)
user_id
=
models
.
CharField
(
max_length
=
50
)
email
=
models
.
CharField
(
max_length
=
50
)
email
=
models
.
CharField
(
max_length
=
50
)
...
@@ -255,13 +267,16 @@ class SubmitVisitor(models.Model):
...
@@ -255,13 +267,16 @@ class SubmitVisitor(models.Model):
class
ViewStatistics
(
models
.
Model
):
class
ViewStatistics
(
models
.
Model
):
materi
=
models
.
ForeignKey
(
Materi
,
models
.
SET_NULL
,
null
=
True
,
related_name
=
"baca"
)
materi
=
models
.
ForeignKey
(
Materi
,
models
.
SET_NULL
,
null
=
True
,
related_name
=
"baca"
)
timestamp
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
timestamp
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
class
DownloadStatistics
(
models
.
Model
):
class
DownloadStatistics
(
models
.
Model
):
materi
=
models
.
ForeignKey
(
Materi
,
models
.
SET_NULL
,
null
=
True
,
related_name
=
"unduh"
)
materi
=
models
.
ForeignKey
(
downloader
=
models
.
ForeignKey
(
User
,
models
.
SET_NULL
,
blank
=
True
,
null
=
True
,
related_name
=
"riwayat_unduh"
)
Materi
,
models
.
SET_NULL
,
null
=
True
,
related_name
=
"unduh"
)
downloader
=
models
.
ForeignKey
(
User
,
models
.
SET_NULL
,
blank
=
True
,
null
=
True
,
related_name
=
"riwayat_unduh"
)
timestamp
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
timestamp
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
...
@@ -302,27 +317,39 @@ class Rating(models.Model):
...
@@ -302,27 +317,39 @@ class Rating(models.Model):
class
RatingContributor
(
models
.
Model
):
class
RatingContributor
(
models
.
Model
):
timestamp
=
models
.
DateTimeField
(
auto_now
=
True
)
timestamp
=
models
.
DateTimeField
(
auto_now
=
True
)
score
=
models
.
PositiveIntegerField
(
validators
=
[
MinValueValidator
(
1
),
MaxValueValidator
(
5
)])
score
=
models
.
PositiveIntegerField
(
contributor
=
models
.
ForeignKey
(
User
,
on_delete
=
models
.
CASCADE
,
related_name
=
'contributor'
)
validators
=
[
MinValueValidator
(
1
),
MaxValueValidator
(
5
)])
user
=
models
.
ForeignKey
(
User
,
on_delete
=
models
.
CASCADE
,
related_name
=
'user'
)
contributor
=
models
.
ForeignKey
(
User
,
on_delete
=
models
.
CASCADE
,
related_name
=
'contributor'
)
user
=
models
.
ForeignKey
(
User
,
on_delete
=
models
.
CASCADE
,
related_name
=
'user'
)
def
save
(
self
,
force_insert
=
False
,
force_update
=
False
,
using
=
None
,
update_fields
=
None
):
def
save
(
self
,
force_insert
=
False
,
force_update
=
False
,
using
=
None
,
update_fields
=
None
):
if
1
<=
self
.
score
<=
5
:
if
1
<=
self
.
score
<=
5
:
super
().
save
(
force_insert
,
force_update
,
using
,
update_fields
)
super
().
save
(
force_insert
,
force_update
,
using
,
update_fields
)
else
:
else
:
raise
ValidationError
(
"Rating score must be integer between 1-5"
)
raise
ValidationError
(
"Rating score must be integer between 1-5"
)
class
Meta
:
class
Meta
:
unique_together
=
[
"contributor"
,
"user"
]
unique_together
=
[
"contributor"
,
"user"
]
class
LaporanMateri
(
models
.
Model
):
class
LaporanMateri
(
models
.
Model
):
materi
=
models
.
ForeignKey
(
Materi
,
on_delete
=
models
.
CASCADE
,
max_length
=
120
)
materi
=
models
.
ForeignKey
(
Materi
,
on_delete
=
models
.
CASCADE
,
max_length
=
120
)
user
=
models
.
ForeignKey
(
User
,
on_delete
=
models
.
CASCADE
)
user
=
models
.
ForeignKey
(
User
,
on_delete
=
models
.
CASCADE
)
laporan
=
models
.
TextField
(
validators
=
[
MinValueValidator
(
30
),
MaxValueValidator
(
120
)],
default
=
""
)
laporan
=
models
.
TextField
(
validators
=
[
MinValueValidator
(
30
),
MaxValueValidator
(
120
)],
default
=
""
)
timestamp
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
timestamp
=
models
.
DateTimeField
(
default
=
timezone
.
now
)
is_rejected
=
models
.
BooleanField
(
default
=
False
)
is_rejected
=
models
.
BooleanField
(
default
=
False
)
class
GuestBook
(
models
.
Model
):
name
=
models
.
TextField
(
default
=
''
)
job
=
models
.
TextField
(
default
=
''
)
gender
=
models
.
CharField
(
max_length
=
6
,
default
=
"Male"
)
class
ReadLater
(
models
.
Model
):
class
ReadLater
(
models
.
Model
):
materi
=
models
.
ForeignKey
(
Materi
,
on_delete
=
models
.
CASCADE
)
materi
=
models
.
ForeignKey
(
Materi
,
on_delete
=
models
.
CASCADE
)
user
=
models
.
ForeignKey
(
User
,
on_delete
=
models
.
CASCADE
)
user
=
models
.
ForeignKey
(
User
,
on_delete
=
models
.
CASCADE
)
...
@@ -331,13 +358,16 @@ class ReadLater(models.Model):
...
@@ -331,13 +358,16 @@ class ReadLater(models.Model):
class
Meta
:
class
Meta
:
unique_together
=
[
"materi"
,
"user"
]
unique_together
=
[
"materi"
,
"user"
]
class
NotifikasiKontributor
(
models
.
Model
):
class
NotifikasiKontributor
(
models
.
Model
):
materi
=
models
.
ForeignKey
(
Materi
,
on_delete
=
models
.
CASCADE
,
max_length
=
120
)
materi
=
models
.
ForeignKey
(
Materi
,
on_delete
=
models
.
CASCADE
,
max_length
=
120
)
user
=
models
.
ForeignKey
(
User
,
on_delete
=
models
.
CASCADE
)
user
=
models
.
ForeignKey
(
User
,
on_delete
=
models
.
CASCADE
)
feedback
=
models
.
CharField
(
max_length
=
20
,
default
=
""
)
feedback
=
models
.
CharField
(
max_length
=
20
,
default
=
""
)
def
__str__
(
self
):
def
__str__
(
self
):
return
"Status: {}"
.
format
(
self
.
feedback
)
return
"Status: {}"
.
format
(
self
.
feedback
)
class
AdminNotification
(
models
.
Model
):
class
AdminNotification
(
models
.
Model
):
materi
=
models
.
ForeignKey
(
Materi
,
on_delete
=
models
.
CASCADE
)
materi
=
models
.
ForeignKey
(
Materi
,
on_delete
=
models
.
CASCADE
)
app/static/app/css/guest_book.css
0 → 100644
View file @
8e7e67a9
.form-style-6
{
font
:
95%
Arial
,
Helvetica
,
sans-serif
;
max-width
:
500px
;
margin
:
25px
auto
;
padding
:
16px
;
background
:
#F7F7F7
;
align-content
:
center
;
justify-content
:
center
;
}
.form-style-6
h1
{
background
:
#43D1AF
;
padding
:
20px
0
;
font-size
:
140%
;
font-weight
:
300
;
text-align
:
center
;
color
:
#fff
;
margin
:
-16px
-16px
16px
-16px
;
}
.form-style-6
input
[
type
=
"text"
],
.form-style-6
select
{
-webkit-transition
:
all
0.30s
ease-in-out
;
-moz-transition
:
all
0.30s
ease-in-out
;
-ms-transition
:
all
0.30s
ease-in-out
;
-o-transition
:
all
0.30s
ease-in-out
;
outline
:
none
;
box-sizing
:
border-box
;
-webkit-box-sizing
:
border-box
;
-moz-box-sizing
:
border-box
;
width
:
100%
;
background
:
#fff
;
margin-bottom
:
4%
;
border
:
1px
solid
#ccc
;
color
:
#555
;
font
:
95%
Arial
,
Helvetica
,
sans-serif
;
}
.form-style-6
input
[
type
=
"text"
]
:focus
,
.form-style-6
textarea
:focus
,
.form-style-6
select
:focus
{
box-shadow
:
0
0
5px
#43D1AF
;
padding
:
3%
;
border
:
1px
solid
#43D1AF
;
}
.form-style-6
button
[
type
=
"submit"
]
{
box-sizing
:
border-box
;
-webkit-box-sizing
:
border-box
;
-moz-box-sizing
:
border-box
;
width
:
100%
;
padding
:
3%
;
background
:
#1da1f2
;
border-bottom
:
2px
solid
#1da1f2
;
border-top-style
:
none
;
border-right-style
:
none
;
border-left-style
:
none
;
color
:
#fff
;
}
.form-style-6
input
[
type
=
"submit"
]
:hover
,
.form-style-6
input
[
type
=
"button"
]
:hover
{
background
:
#2EBC99
;
}
\ No newline at end of file
app/templates/guest_book.html
0 → 100644
View file @
8e7e67a9
{% extends 'base.html' %}
{% load static %}
{% block title %}Digipus Home{% endblock %}
{% block header %}
<!DOCTYPE html>
<html
lang=
"en"
>
<head>
<title>
Digipus Home
</title>
<meta
charset=
"UTF-8"
>
<meta
name=
"viewport"
content=
"width=device-width, inital-scale=1"
>
<link
rel=
"stylesheet"
type=
"text/css"
href=
"{% static 'app/css/guest_book.css' %}"
>
{% endblock header %}
</head>
<!-- Page Content -->
{% block content %}
<div
class=
"form-group form-style-6"
id=
"myForm"
>
<form
method=
"POST"
>
<h3
style=
"text-align: center;"
>
Buku Tamu Non Anggota
</h3>
{% csrf_token %}
{{form}}
<button
type=
"submit"
class=
"btn btn-success"
>
Submit
</button>
</form>
</div>
{% endblock %}
</html>
\ No newline at end of file
app/tests.py
View file @
8e7e67a9
This diff is collapsed.
Click to expand it.
app/urls.py
View file @
8e7e67a9
...
@@ -5,8 +5,7 @@ from app import views
...
@@ -5,8 +5,7 @@ from app import views
from
app.views
import
(
DashboardKontributorView
,
ProfilView
,
StatisticsView
,
from
app.views
import
(
DashboardKontributorView
,
ProfilView
,
StatisticsView
,
SuksesLoginAdminView
,
SuksesLoginKontributorView
,
DownloadHistoryView
,