Skip to content

Commit

Permalink
CB-244: Frontend for the rating system
Browse files Browse the repository at this point in the history
  • Loading branch information
ps2611 authored and gentlecat committed Sep 5, 2017
1 parent 972dd5a commit 078cbc2
Show file tree
Hide file tree
Showing 19 changed files with 201 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def browse_release_groups(*, artist_id, release_types=None, limit=None, offset=N
for release_group in release_groups:
includes_data[release_group.id]['meta'] = release_group.meta
release_groups = ([to_dict_release_groups(release_group, includes_data[release_group.id])
for release_group in release_groups], count)
for release_group in release_groups], count)
cache.set(key=key, val=release_groups, time=DEFAULT_CACHE_EXPIRATION)
return release_groups

Expand Down
17 changes: 13 additions & 4 deletions critiquebrainz/frontend/forms/review.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from flask_wtf import Form
from flask_babel import lazy_gettext, Locale
from wtforms import TextAreaField, RadioField, SelectField, BooleanField, StringField, validators
from wtforms import TextAreaField, RadioField, SelectField, BooleanField, StringField, validators, IntegerField
from wtforms.validators import ValidationError
from wtforms.widgets import HiddenInput
from wtforms.widgets import HiddenInput, Input
from babel.core import UnknownLocaleError
from critiquebrainz.db.review import supported_languages
import pycountry
Expand Down Expand Up @@ -32,9 +32,9 @@ def __call__(self, form, field):
class ReviewEditForm(Form):
state = StringField(widget=HiddenInput(), default='draft', validators=[validators.DataRequired()])
text = TextAreaField(lazy_gettext("Text"), [
validators.DataRequired(message=lazy_gettext("Review is empty!")),
validators.Optional(),
StateAndLength(min=MIN_REVIEW_LENGTH, max=MAX_REVIEW_LENGTH,
message=lazy_gettext("Review length needs to be between %(min)d and %(max)d characters.",
message=lazy_gettext("Text length needs to be between %(min)d and %(max)d characters.",
min=MIN_REVIEW_LENGTH, max=MAX_REVIEW_LENGTH))])
license_choice = RadioField(
choices=[
Expand All @@ -43,12 +43,21 @@ class ReviewEditForm(Form):
],
validators=[validators.DataRequired(message=lazy_gettext("You need to choose a license!"))])
language = SelectField(lazy_gettext("You need to accept the license agreement!"), choices=languages)
rating = IntegerField(lazy_gettext("Rating"), widget=Input(input_type='number'), validators=[validators.Optional()])

def __init__(self, default_license_id='CC BY-SA 3.0', default_language='en', **kwargs):
kwargs.setdefault('license_choice', default_license_id)
kwargs.setdefault('language', default_language)
Form.__init__(self, **kwargs)

def validate(self):
if not super(ReviewEditForm, self).validate():
return False
if not self.text.data and not self.rating.data:
self.text.errors.append("You must provide some text or a rating to complete this review.")
return False
return True


class ReviewCreateForm(ReviewEditForm):
agreement = BooleanField(validators=[
Expand Down
4 changes: 4 additions & 0 deletions critiquebrainz/frontend/static/gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,15 @@ function buildScripts() {
let spotifyBundle = runYarb('spotify.js', function (b) {
b.external(commonBundle);
});
let ratingBundle = runYarb('rating.js', function (b) {
b.external(commonBundle);
});

return Q.all([
writeScript(commonBundle, 'common.js'),
writeScript(leafletBundle, 'leaflet.js'),
writeScript(spotifyBundle, 'spotify.js'),
writeScript(ratingBundle, 'rating.js'),
]).then(writeManifest);
}

Expand Down
5 changes: 5 additions & 0 deletions critiquebrainz/frontend/static/scripts/rating.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require('bootstrap-rating-input');

$.fn.rating.Constructor.DEFAULTS['empty-value'] = null;
$.fn.rating.Constructor.DEFAULTS['clearable'] = '';
$.fn.rating.Constructor.DEFAULTS['clearableIcon'] = 'glyphicon-remove-circle';
25 changes: 25 additions & 0 deletions critiquebrainz/frontend/static/styles/main.less
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,13 @@ ul.sharing {
}
}


// Entity page
.avg-rating {
margin-bottom: 10px;
}


// Release group page
a#write-review { margin-top: 20px; }
a#edit-review { margin-top: 20px; }
Expand Down Expand Up @@ -532,3 +539,21 @@ a#edit-review { margin-top: 20px; }
height: 20px;
}
}


// Rating star icons
.glyphicon-star, .glyphicon-star-empty {
color: #DAA520;
font-size: 18px;
}


// Remove rating icon
.glyphicon-remove-circle {
color: #808080;
font-size: 16px;
}

.glyphicon-remove-circle:hover {
color: red;
}
7 changes: 7 additions & 0 deletions critiquebrainz/frontend/templates/event/entity.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{% extends 'base.html' %}
{% from 'macros.html' import show_avg_rating with context %}

{% block title %}{{ event.name }} - CritiqueBrainz{% endblock %}

Expand Down Expand Up @@ -102,6 +103,12 @@ <h4 style="margin-bottom:0;">{{ _('Reviews') }}</h4>
<div class="col-md-3">
<h4>{{ _('Event information') }}</h4>

{% if avg_rating %}
<div class="avg-rating">
{{ show_avg_rating(avg_rating.rating, avg_rating.count) }}
</div>
{% endif %}

{% if 'release_group-rels' in event %}
{% for rg in event['release_group-rels'] %}
<p><b>{{ rg['type'] | title }}:</b> <a href="{{ url_for('release_group.entity', id=rg['release_group']['id']) }}">{{ rg['release_group']['title'] }}</a></p>
Expand Down
5 changes: 5 additions & 0 deletions critiquebrainz/frontend/templates/macros.html
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,8 @@
{{ spotify_player(spotify_release_group_id, spotify_mappings, spotify_show_add_message) }}
{% endif %}
{% endmacro %}

{% macro show_avg_rating(rating, count) %}
<i class="glyphicon glyphicon-star"></i>
<span style="font-size: 16px;">{{ rating }}</span><em class="text-muted">/5 {{ _('based on %(number)s ratings', number=count) }}</em>
{% endmacro %}
6 changes: 6 additions & 0 deletions critiquebrainz/frontend/templates/place/entity.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{% extends 'base.html' %}
{% from 'macros.html' import show_avg_rating with context %}

{% block title %}{{ place.name }} - CritiqueBrainz{% endblock %}

Expand Down Expand Up @@ -92,6 +93,11 @@ <h4 style="margin-bottom:0;">{{ _('Reviews') }}</h4>

<div class="col-md-3">
<h4>{{ _('Place information') }}</h4>
{% if avg_rating %}
<div class="avg-rating">
{{ show_avg_rating(avg_rating.rating, avg_rating.count) }}
</div>
{% endif %}
{% if place['external-urls'] %}
<b>{{ _('External links') }}</b>
<ul class="list-unstyled external-links">
Expand Down
7 changes: 6 additions & 1 deletion critiquebrainz/frontend/templates/release_group/entity.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends 'base.html' %}
{% from 'macros.html' import cover_art, show_embedded_player, show_tags with context %}
{% from 'macros.html' import cover_art, show_embedded_player, show_tags, show_avg_rating with context %}

{% block title %}
{{ _('Release group "%(title)s" by %(artist)s', title=release_group.title, artist=release_group['artist-credit-phrase']) }}
Expand Down Expand Up @@ -138,6 +138,11 @@ <h4>{{ _('Tracklist') }}</h4>

<div class="col-md-3">
<h4>{{ _('Release group information') }}</h4>
{% if avg_rating %}
<div class="avg-rating">
{{ show_avg_rating(avg_rating.rating, avg_rating.count) }}
</div>
{% endif %}
<p>
{% if release_group['first-release-date'] %}{{ _('First release in %(year)s', year=release_group['first-release-date'][:4]) }}{% endif %}
</p>
Expand Down
11 changes: 11 additions & 0 deletions critiquebrainz/frontend/templates/review/compare.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,25 @@
<h3>{{ _('Revision') }} {{ left['number'] }}</h3>
<p class="small">{{ _('as of') }} {{ left['timestamp'] | datetime }}</p>
</div>
{% if left.rating or right.rating %}
<input type="number" class="rating" id="rating" value={{ left['rating'] }} data-readonly data-inline />
{% endif %}
<div class="diff-content">{{ left['text'] | safe }}</div>
</div>
<div class="col-md-6">
<div class="diff-header">
<h3>{{ _('Revision') }} {{ right['number'] }}</h3>
<p class="small">{{ _('as of') }} {{ right['timestamp'] | datetime }}</p>
</div>
{% if right.rating or left.rating %}
<input type="number" class="rating" id="rating" value={{ right['rating'] }} data-readonly data-inline />
{% endif %}
<div class="diff-content">{{ right['text'] | safe }}</div>
</div>
</div>
{% endblock %}

{% block scripts %}
{{ super() }}
<script src="{{ get_static_path('rating.js') }}"></script>
{% endblock %}
76 changes: 51 additions & 25 deletions critiquebrainz/frontend/templates/review/entity/base.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
{% extends 'base.html' %}
{% from 'macros.html' import review_credit, cover_art with context %}
{% from 'macros.html' import review_credit, cover_art, show_avg_rating with context %}

{% block content %}
<div id="review-entity">
{% block entity_title %}{% endblock %}
{% if avg_rating %}
{{ show_avg_rating(avg_rating.rating, avg_rating.count) }}
{% endif %}

<div id="review-content" class="row">
{% block sidebar %}{% endblock %}
Expand All @@ -15,19 +18,33 @@ <h3>{{ review_credit(review, user_picture_size=24) }}</h3>
<div class="alert alert-warning" role="alert">{{ _('<b>This review has not been published yet!</b> Only you can see it.') }}</div>
{% else %}
<em class="text-muted">{{ _('Published on %(date)s', date = review.created|date) }}</em><br />
<em class="text-muted">{{ _('Written in %(language)s', language = review.language|language_name) }}</em><br />
{% set votes_total = review.votes.positive + review.votes.negative %}
<em class="text-muted">
{% if votes_total > 0 %}
{{ _('%s out of %s people have found this review useful' % (review.votes.positive, votes_total)) }}
{% else %}
{{ _('This review has not yet been rated') }}
{% endif %}
</em>
{% if review.text %}
<em class="text-muted">{{ _('Written in %(language)s', language = review.language|language_name) }}</em><br />
{% set votes_total = review.votes.positive + review.votes.negative %}
<em class="text-muted">
{% if votes_total > 0 %}
{{ _('%s out of %s people have found this review useful' % (review.votes.positive, votes_total)) }}
{% else %}
{{ _('This review has not yet been rated') }}
{% endif %}
</em>
{% endif %}
{% endif %}

<p style="word-wrap: break-word; white-space: pre-wrap;">{{ review.text_html|safe }}</p>

{% if review.rating %}
<div style="margin-top: 15px;">
<input type="number" class="rating" id="rating" value={{ review.rating }} data-readonly data-inline />
</div>
{% endif %}
{% if review.text %}
<!-- Used to display only-text review properly -->
{% if review.rating %}
<p style="word-wrap: break-word; white-space: pre-wrap; display: inline;">{{ review.text_html|safe }}</p>
{% else %}
<p style="word-wrap: break-word; white-space: pre-wrap;">{{ review.text_html|safe }}</p>
{% endif %}
{% endif %}

<hr />

{% if not review.is_draft %}
Expand All @@ -54,20 +71,22 @@ <h3>{{ review_credit(review, user_picture_size=24) }}</h3>
{% if current_user != review.user %}
<div class="row">
<div class="col-md-8">
<form method="POST" action="{{ url_for('review.vote_submit', review_id=review.id) }}">
{% if vote == None %}
{{ _('Did you find this review useful?') }}
<button type="submit" class="btn btn-default btn-xs {% if vote.vote == False %}disabled{% endif %}" name="yes">{{ _('Yes') }}</button>
<button type="submit" class="btn btn-default btn-xs {% if vote.vote == True %}disabled{% endif %}" name="no">{{ _('No') }}</button>
{% else %}
{% if vote.vote == True %}
<span class="text-success">{{ _('You found this review useful.') }}</span>
{% if review.text %}
<form method="POST" action="{{ url_for('review.vote_submit', review_id=review.id) }}">
{% if vote == None %}
{{ _('Did you find this review useful?') }}
<button type="submit" class="btn btn-default btn-xs {% if vote.vote == False %}disabled{% endif %}" name="yes">{{ _('Yes') }}</button>
<button type="submit" class="btn btn-default btn-xs {% if vote.vote == True %}disabled{% endif %}" name="no">{{ _('No') }}</button>
{% else %}
<span class="text-warning">{{ _('You didn\'t find this review useful.') }}</span>
{% if vote.vote == True %}
<span class="text-success">{{ _('You found this review useful.') }}</span>
{% else %}
<span class="text-warning">{{ _('You didn\'t find this review useful.') }}</span>
{% endif %}
<a href="{{ url_for('review.vote_delete', id=review.id) }}" class="btn btn-default btn-xs" title="{{ _('Delete your vote') }}"><span class="glyphicon glyphicon-trash"></span></a>
{% endif %}
<a href="{{ url_for('review.vote_delete', id=review.id) }}" class="btn btn-default btn-xs" title="{{ _('Delete your vote') }}"><span class="glyphicon glyphicon-trash"></span></a>
{% endif %}
</form>
</form>
{% endif %}
</div>
<div class="col-md-4" id="reporting">
<a href="{{ url_for('review.report', id=review.id) }}" class="text-danger">{{ _('Report abuse') }}</a>
Expand All @@ -89,7 +108,9 @@ <h3>{{ review_credit(review, user_picture_size=24) }}</h3>
{% else %}
<div class="row">
<div class="col-md-5">
{{ _('<a href="%(link)s">Sign in</a> to rate this review', link=url_for('login.index', next=url_for('review.entity', id=review.id))) }}
{% if review.text %}
{{ _('<a href="%(link)s">Sign in</a> to rate this review', link=url_for('login.index', next=url_for('review.entity', id=review.id))) }}
{% endif %}
</div>
<div class="col-md-4 col-md-offset-3">
<a id="old-revisions" href="{{ url_for('review.revisions', id=review.id) }}">{{ _('View older revisions') }}</a>
Expand Down Expand Up @@ -124,3 +145,8 @@ <h3>
</div>
</div>
{% endblock %}

{% block scripts %}
{{ super() }}
<script src="{{ get_static_path('rating.js') }}"></script>
{% endblock %}
27 changes: 17 additions & 10 deletions critiquebrainz/frontend/templates/review/modify/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,24 @@
<div class="row">
{% include ('review/modify/%s.html' % entity_type) %}
</div>
<div class="clearfix">
<ul class="nav nav-tabs" style="margin-bottom:-1px;">
<li class="active"><a href="#write" data-toggle="tab">{{ _('Write') }}</a></li>
<li><a href="#preview" data-toggle="tab">{{ _('Preview') }}</a></li>
<small class="text-muted pull-right" style="margin-top:20px;font-style:italic;">
{{ _('You can use <a href="%(url)s" target="_blank">Markdown</a> syntax to apply custom formatting.',
url='http://daringfireball.net/projects/markdown/syntax') }}
</small>
</ul>
</div>
<hr />

<form id="review-editor" method="POST" class="form-horizontal" role="form">
{{ form.hidden_tag() }}
<div class="form-group">
<label class="col-sm-1" for="rating">{{ _('Rating:') }}</label>
<div class="col-sm-2">{{ form.rating(class="rating", id="rating") }}</div>
</div>
<div class="clearfix">
<ul class="nav nav-tabs" style="margin-bottom:-1px;">
<li class="active"><a href="#write" data-toggle="tab">{{ _('Write') }}</a></li>
<li><a href="#preview" data-toggle="tab">{{ _('Preview') }}</a></li>
<small class="text-muted pull-right" style="margin-top:20px;font-style:italic;">
{{ _('You can use <a href="%(url)s" target="_blank">Markdown</a> syntax to apply custom formatting.',
url='http://daringfireball.net/projects/markdown/syntax') }}
</small>
</ul>
</div>
<div class="form-group">
<div class="col-sm-12 tab-content">
<div class="tab-pane fade in active" id="write">
Expand Down Expand Up @@ -108,4 +114,5 @@
});
});
</script>
<script src="{{ get_static_path('rating.js') }}"></script>
{% endblock %}
9 changes: 9 additions & 0 deletions critiquebrainz/frontend/views/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import critiquebrainz.db.avg_rating as db_avg_rating
import critiquebrainz.db.exceptions as db_exceptions

def get_avg_rating(entity_id, entity_type):
"""Retrieve average rating"""
try:
return db_avg_rating.get(entity_id, entity_type)
except db_exceptions.NoDataFoundException:
return None
4 changes: 3 additions & 1 deletion critiquebrainz/frontend/views/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import critiquebrainz.db.review as db_review
import critiquebrainz.frontend.external.musicbrainz_db.event as mb_event
import critiquebrainz.frontend.external.musicbrainz_db.exceptions as mb_exceptions
from critiquebrainz.frontend.views import get_avg_rating


event_bp = Blueprint('event', __name__)
Expand Down Expand Up @@ -37,6 +38,7 @@ def entity(id):
offset = int(request.args.get('offset', default=0))
reviews, count = db_review.list_reviews(entity_id=event['id'], entity_type='event', sort='popularity',
limit=limit, offset=offset)
avg_rating = get_avg_rating(event['id'], "event")

return render_template('event/entity.html', id=event['id'], event=event, reviews=reviews,
my_review=my_review, limit=limit, offset=offset, count=count)
my_review=my_review, limit=limit, offset=offset, count=count, avg_rating=avg_rating)

0 comments on commit 078cbc2

Please sign in to comment.