Skip to content

Commit

Permalink
Merge pull request #392 from hasgeek/labels
Browse files Browse the repository at this point in the history
Implement labels (resolves #313)
  • Loading branch information
jace authored Apr 26, 2019
2 parents 15181a0 + 7592c8f commit 73ad164
Show file tree
Hide file tree
Showing 43 changed files with 2,442 additions and 440 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ install:
- pip install -U pip wheel
- pip install -r requirements.txt
- pip install -r test_requirements.txt
- make
script:
- ./runtests.sh
after_success:
Expand Down
19 changes: 19 additions & 0 deletions funnel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@
app.assets.register('css_leaflet',
Bundle(assets.require('leaflet.css', 'leaflet-search.css'),
output='css/leaflet.packed.css', filters='cssmin'))
app.assets.register('js_emojionearea',
Bundle(assets.require('!jquery.js', 'emojionearea-material.js'),
output='js/emojionearea.packed.js', filters='uglipyjs'))
app.assets.register('css_emojionearea',
Bundle(assets.require('emojionearea-material.css'),
output='css/emojionearea.packed.css', filters='cssmin'))
app.assets.register('js_sortable',
Bundle(assets.require('!jquery.js', 'jquery.ui.js', 'jquery.ui.sortable.touch.js'),
output='js/sortable.packed.js', filters='uglipyjs'))


baseframe.init_app(funnelapp, requires=['funnel'], ext_requires=[
'pygments', 'toastr', 'baseframe-mui'], theme='mui')
Expand Down Expand Up @@ -133,6 +143,15 @@
funnelapp.assets.register('css_leaflet',
Bundle(assets.require('leaflet.css', 'leaflet-search.css'),
output='css/leaflet.packed.css', filters='cssmin'))
funnelapp.assets.register('js_emojionearea',
Bundle(assets.require('!jquery.js', 'emojionearea-material.js'),
output='js/emojionearea.packed.js', filters='uglipyjs'))
funnelapp.assets.register('css_emojionearea',
Bundle(assets.require('emojionearea-material.css'),
output='css/emojionearea.packed.css', filters='cssmin'))
funnelapp.assets.register('js_sortable',
Bundle(assets.require('!jquery.js', 'jquery.ui.js', 'jquery.ui.sortable.touch.js'),
output='js/sortable.packed.js', filters='uglipyjs'))

# FIXME: Hack for external build system generating relative /static URLs.
# Fix this by generating absolute URLs to the static subdomain during build.
Expand Down
63 changes: 63 additions & 0 deletions funnel/assets/js/proposal.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,72 @@ export const Video = {
},
};

export const LabelsWidget = {
init() {
const Widget = this;
// On load, if the radio has been selected, then check mark the listwidget label
$('.listwidget input[type="radio"]').each(function() {
if(this.checked) {
$(this).siblings().find('.mui-form__label').addClass('checked');
}
});

$('.listwidget .mui-form__label').click(function() {
if($(this).hasClass('checked')) {
$(this).removeClass('checked');
$(this).siblings().find('input[type="radio"]').prop('checked', false);
Widget.updateLabels('', $(this).text().trim(), false);
} else {
$(this).addClass('checked');
$(this).siblings().find('input[type="radio"]').first().click();
}
});

// Add check mark to listwidget label
$('.listwidget input[type="radio"]').change(function() {
let label = $(this).parent().parent().prev('.mui-form__label');
label.addClass('checked');
let labelTxt = `${label.text()}: ${$(this).parent().find('label').text()}`.trim();
let attr = label.text().trim();
Widget.updateLabels(labelTxt, attr, this.checked);
});

$('.add-label-form input[type="checkbox"]').change(function() {
let labelTxt = $(this).parent('label').text().trim();
Widget.updateLabels(labelTxt, labelTxt, this.checked);
});

// Open and close dropdown
$(document).on('click', function(event) {
if ($('#label-select')[0] === event.target || !$(event.target).parents('#label-dropdown').length) {
Widget.handleDropdown();
}
});
},
handleDropdown() {
if($('#label-dropdown fieldset').hasClass('active')) {
$('#label-dropdown fieldset').removeClass('active');
} else {
$('#label-dropdown fieldset').addClass('active');
}
},
updateLabels(label='', attr='', action=true) {
if(action) {
if(label !== attr) {
$(`.label[data-labeltxt="${attr}"]`).remove();
}
let span = `<span class="label mui--text-caption mui--text-bold" data-labeltxt="${attr}">${label}</span>`;
$('#label-select').append(span);
} else {
$(`.label[data-labeltxt="${attr}"]`).remove();
}
}
};

$(() => {
window. HasGeek.ProposalInit = function ({pageUrl, videoWrapper= '', videoUrl= ''}) {
Comments.init(pageUrl);
LabelsWidget.init();

if (videoWrapper) {
Video.embedIframe(videoWrapper, videoUrl);
Expand Down
1 change: 1 addition & 0 deletions funnel/forms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
from .session import *
from .profile import *
from .participant import *
from .label import *
25 changes: 25 additions & 0 deletions funnel/forms/label.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-

from baseframe import __
import baseframe.forms as forms

__all__ = ['LabelForm', 'LabelOptionForm']


class LabelForm(forms.Form):
name = forms.StringField("", widget=forms.HiddenInput(), validators=[forms.validators.Optional()])
title = forms.StringField(__("Label"),
validators=[forms.validators.DataRequired(__(u"This can’t be empty")), forms.validators.Length(max=250)])
icon_emoji = forms.StringField("")
required = forms.BooleanField(__("Make this label mandatory in proposal forms"), default=False,
description=__("If checked, proposers must select one of the options"))
restricted = forms.BooleanField(__("Restrict use of this label to editors"), default=False,
description=__("If checked, only editors and reviewers can apply this label on proposals"))


class LabelOptionForm(forms.Form):
name = forms.StringField("", widget=forms.HiddenInput(), validators=[forms.validators.Optional()])
title = forms.StringField(__("Option"),
validators=[forms.validators.DataRequired(__(u"This can’t be empty")), forms.validators.Length(max=250)])
icon_emoji = forms.StringField("")
seq = forms.IntegerField("", widget=forms.HiddenInput())
82 changes: 66 additions & 16 deletions funnel/forms/proposal.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,81 @@
from baseframe.forms.sqlalchemy import QuerySelectField
from ..models import Project, Profile

__all__ = ['TransferProposal', 'ProposalForm', 'ProposalTransitionForm', 'ProposalMoveForm']
__all__ = ['TransferProposal', 'ProposalForm', 'ProposalTransitionForm', 'ProposalLabelsForm',
'ProposalMoveForm', 'ProposalLabelsAdminForm']


def proposal_label_form(project, proposal):
"""
Returns a label form for the given project and proposal.
"""
class ProposalLabelForm(forms.Form):
pass

for label in project.labels:
if label.has_options and not label.archived and not label.restricted:
setattr(ProposalLabelForm, label.name, forms.RadioField(
label.form_label_text,
description=label.description,
validators=[forms.validators.DataRequired(__("Please select one"))] if label.required else [],
choices=[(option.name, option.title) for option in label.options if not option.archived]
))

return ProposalLabelForm(obj=proposal.formlabels if proposal else None, meta={'csrf': False})


def proposal_label_admin_form(project, proposal):
"""
Returns a label form to use in admin panel for given project and proposal
"""
class ProposalLabelAdminForm(forms.Form):
pass

for label in project.labels:
if not label.archived and (label.restricted or not label.has_options):
form_kwargs = {}
if label.has_options:
FieldType = forms.RadioField
form_kwargs['choices'] = [(option.name, option.title) for option in label.options if not option.archived]
else:
FieldType = forms.BooleanField

setattr(ProposalLabelAdminForm, label.name, FieldType(
label.form_label_text,
description=label.description,
validators=[forms.validators.DataRequired(__("Please select one"))] if label.required else [],
**form_kwargs
))

return ProposalLabelAdminForm(obj=proposal.formlabels if proposal else None, meta={'csrf': False})


class TransferProposal(forms.Form):
userid = forms.UserSelectField(__("Transfer to"), validators=[forms.validators.DataRequired()])


class ProposalLabelsForm(forms.Form):
formlabels = forms.FormField(forms.Form, __("Labels"))

def set_queries(self):
self.formlabels.form = proposal_label_form(project=self.edit_parent, proposal=self.edit_obj)


class ProposalLabelsAdminForm(forms.Form):
formlabels = forms.FormField(forms.Form, __("Labels"))

def set_queries(self):
self.formlabels.form = proposal_label_admin_form(project=self.edit_parent, proposal=self.edit_obj)


class ProposalForm(forms.Form):
speaking = forms.RadioField(__("Are you speaking?"), coerce=int,
choices=[(1, __(u"I will be speaking")),
(0, __(u"I’m proposing a topic for someone to speak on"))])
title = forms.StringField(__("Title"), validators=[forms.validators.DataRequired()],
description=__("The title of your session"))
section = QuerySelectField(__("Section"), get_label='title', validators=[forms.validators.DataRequired()],
widget=forms.ListWidget(prefix_label=False), option_widget=forms.RadioInput())
objective = forms.MarkdownField(__("Objective"), validators=[forms.validators.DataRequired()],
description=__("What is the expected benefit for someone attending this?"))
session_type = forms.RadioField(__("Session type"), validators=[forms.validators.DataRequired()], choices=[
('Lecture', __("Lecture")),
('Demo', __("Demo")),
('Tutorial', __("Tutorial")),
('Workshop', __("Workshop")),
('Discussion', __("Discussion")),
('Panel', __("Panel")),
])
technical_level = forms.RadioField(__("Technical level"), validators=[forms.validators.DataRequired()], choices=[
('Beginner', __("Beginner")),
('Intermediate', __("Intermediate")),
('Advanced', __("Advanced")),
])
description = forms.MarkdownField(__("Description"), validators=[forms.validators.DataRequired()],
description=__("A detailed description of the session"))
requirements = forms.MarkdownField(__("Requirements"),
Expand Down Expand Up @@ -63,6 +108,8 @@ class ProposalForm(forms.Form):
location = forms.StringField(__("Your location"), validators=[forms.validators.DataRequired(), forms.validators.Length(max=80)],
description=__("Your location, to help plan for your travel if required"))

formlabels = forms.FormField(forms.Form, __("Labels"))

def __init__(self, *args, **kwargs):
super(ProposalForm, self).__init__(*args, **kwargs)
project = kwargs.get('parent')
Expand All @@ -75,6 +122,9 @@ def __init__(self, *args, **kwargs):
if project.proposal_part_b.get('hint'):
self.description.description = project.proposal_part_b.get('hint')

def set_queries(self):
self.formlabels.form = proposal_label_form(project=self.edit_parent, proposal=self.edit_obj)


class ProposalTransitionForm(forms.Form):
transition = forms.SelectField(__("Status"), validators=[forms.validators.DataRequired()])
Expand Down
1 change: 1 addition & 0 deletions funnel/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@
from .session import *
from .user import *
from .venue import *
from .label import *
Loading

0 comments on commit 73ad164

Please sign in to comment.