You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by br...@apache.org on 2020/01/06 23:05:09 UTC
[allura] 01/04: [FEATURE] Added ForgeFeedback app
This is an automated email from the ASF dual-hosted git repository.
brondsem pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git
commit 66a29498d8099d5be31c3ea201d7e3f0f7c28a64
Author: Vrinda A <Vr...@in.bosch.com>
AuthorDate: Wed Sep 25 22:44:22 2019 +0530
[FEATURE] Added ForgeFeedback app
- This app will let the user rate and review a project.
---
Allura/allura/model/project.py | 1 +
Allura/allura/templates/jinja_master/nav_menu.html | 26 +++
ForgeFeedback/forgefeedback/__init__.py | 0
ForgeFeedback/forgefeedback/feedback_main.py | 230 +++++++++++++++++++++
ForgeFeedback/forgefeedback/model/__init__.py | 21 ++
ForgeFeedback/forgefeedback/model/feedback.py | 82 ++++++++
.../forgefeedback/nf/feedback/css/feedback.css | 112 ++++++++++
ForgeFeedback/forgefeedback/templates/__init__.py | 0
.../templates/feedback/common_feedback.html | 87 ++++++++
.../templates/feedback/edit_feedback.html | 103 +++++++++
.../templates/feedback/feedback_list.html | 100 +++++++++
.../forgefeedback/templates/feedback/index.html | 56 +++++
.../templates/feedback/new_feedback.html | 79 +++++++
ForgeFeedback/forgefeedback/tests/__init__.py | 17 ++
.../forgefeedback/tests/functional/__init__.py | 17 ++
.../forgefeedback/tests/functional/test_root.py | 69 +++++++
.../forgefeedback/tests/test_feedback_roles.py | 54 +++++
ForgeFeedback/forgefeedback/tests/unit/__init__.py | 51 +++++
.../forgefeedback/tests/unit/test_feedback.py | 41 ++++
.../tests/unit/test_root_controller.py | 80 +++++++
ForgeFeedback/forgefeedback/version.py | 24 +++
ForgeFeedback/setup.py | 53 +++++
requirements.in | 4 +-
requirements.txt | 2 +
24 files changed, 1308 insertions(+), 1 deletion(-)
diff --git a/Allura/allura/model/project.py b/Allura/allura/model/project.py
index 0776e8f..5a6f896 100644
--- a/Allura/allura/model/project.py
+++ b/Allura/allura/model/project.py
@@ -251,6 +251,7 @@ class Project(SearchIndexable, MappedClass, ActivityNode, ActivityObject):
tracking_id = FieldProperty(str, if_missing='')
is_nbhd_project = FieldProperty(bool, if_missing=False)
features = FieldProperty([str])
+ rating = FieldProperty(float, if_missing=0)
# transient properties
notifications_disabled = False
diff --git a/Allura/allura/templates/jinja_master/nav_menu.html b/Allura/allura/templates/jinja_master/nav_menu.html
index 0f956ce..9509334 100644
--- a/Allura/allura/templates/jinja_master/nav_menu.html
+++ b/Allura/allura/templates/jinja_master/nav_menu.html
@@ -59,6 +59,32 @@
<a href="{{ admin.url() }}">{{ admin.username }}</a>{{ ',' if not loop.last }}
{% endif %}
{%- endfor -%}
+ <p id="detailed_ratings"><span id=stars></span></p>
</div>
{% endif %}
{% endif %}
+
+<script>
+ document.getElementById("stars").innerHTML = getStars("{{c.project.rating}}");
+
+function getStars(ratings) {
+
+ // Round to nearest half
+ ratings = Math.round(ratings * 2) / 2;
+ let output = [];
+
+ // Append all the filled whole stars
+ for (var i = ratings; i >= 1; i--)
+ output.push('<i class="fa fa-star" aria-hidden="true" style="color: orange;"></i> ');
+
+ // If there is a half a star, append it
+ if (i == .5) output.push('<i class="fa fa-star-half-o" aria-hidden="true" style="color: orange;"></i> ');
+
+ // Fill the empty stars
+ for (let i = (5 - ratings); i >= 1; i--)
+ output.push('<i class="fa fa-star-o" aria-hidden="true" style="color: orange;"></i> ');
+
+ return output.join('');
+
+ }
+ </script>
diff --git a/ForgeFeedback/forgefeedback/__init__.py b/ForgeFeedback/forgefeedback/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ForgeFeedback/forgefeedback/feedback_main.py b/ForgeFeedback/forgefeedback/feedback_main.py
new file mode 100644
index 0000000..0f9a555
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/feedback_main.py
@@ -0,0 +1,230 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import logging
+import pymongo
+
+# Non-stdlib imports
+
+from tg import expose, flash, url, config, request
+from tg.decorators import with_trailing_slash, without_trailing_slash
+from tg import tmpl_context as c, app_globals as g
+from ming.odm import session
+from ming.orm import session
+
+## profanityfilter package ##
+from profanityfilter import ProfanityFilter
+
+# Pyforge-specific imports
+
+from allura import model as M
+from allura import version
+from allura.app import (
+ Application,
+ )
+from allura.controllers import BaseController
+from allura.controllers.feed import FeedController
+from allura.lib.decorators import require_post
+from allura.lib.security import (require_access, has_access)
+from allura.model import project
+
+# Local imports
+
+from forgefeedback import model as TM
+from forgefeedback import version
+from forgefeedback.model import Feedback
+
+log = logging.getLogger(__name__)
+
+
+class ForgeFeedbackApp(Application):
+
+
+ __version__ = version.__version__
+ permissions = [
+ 'read', 'update', 'create',
+ 'post', 'admin', 'delete'
+ ]
+
+ permissions_desc = {
+ 'read': 'View ratings.',
+ 'update': 'Edit ratings.',
+ 'create': 'Create ratings.',
+ 'admin': 'Set permissions. Configure option.',
+ 'delete': 'Delete and undelete ratings. View deleted ratings.'
+ }
+
+ config_options = Application.config_options + [
+ ]
+ tool_label = 'Feedback'
+ tool_description = """
+ Feedbacks are given for the tools in the form of reviews and ratings, edit and delete the feedback"""
+ default_mount_label = 'Feedback'
+ default_mount_point = 'feedback'
+ ordinal = 8
+
+ def __init__(self, project, config):
+ Application.__init__(self, project, config)
+ self.root = RootController()
+
+ def install(self, project):
+ 'Set up any default permissions and roles here'
+ super(ForgeFeedbackApp, self).install(project)
+ # Setup permissions
+ role_admin = M.ProjectRole.by_name('Admin')._id
+ role_developer = M.ProjectRole.by_name('Developer')._id
+ role_auth = M.ProjectRole.by_name('*authenticated')._id
+ role_anon = M.ProjectRole.by_name('*anonymous')._id
+ self.config.acl = [
+ M.ACE.allow(role_anon, 'read'),
+ M.ACE.allow(role_auth, 'post'),
+ M.ACE.allow(role_auth, 'unmoderated_post'),
+ M.ACE.allow(role_auth, 'create'),
+ M.ACE.allow(role_developer, 'update'),
+ M.ACE.allow(role_developer, 'moderate'),
+ M.ACE.allow(role_developer, 'delete'),
+ M.ACE.allow(role_admin, 'configure'),
+ M.ACE.allow(role_admin, 'admin'),
+ ]
+
+ def uninstall(self, project):
+ """Remove all the tool's artifacts from the database"""
+ app_config_id = {'app_config_id': c.app.config._id}
+ TM.Feedback.query.remove(app_config_id)
+ super(ForgeFeedbackApp, self).uninstall(project)
+
+
+class RootController(BaseController, FeedController):
+
+ def _check_security(self):
+ require_access(c.app, 'read')
+
+ @expose('jinja:forgefeedback:templates/feedback/index.html')
+ def index(self,**kw):
+ require_access(c.app, 'read')
+ user_has_already_reviewed = False
+ rating_by_user = Feedback.query.find({
+ 'reported_by_id' : c.user._id,'project_id' : c.project._id}
+ ).count()
+ if (rating_by_user > 0) :
+ user_has_already_reviewed = True
+ return dict(review_list= self.get_review_list(), user_has_already_reviewed = user_has_already_reviewed, rating=self.getRating())
+
+ # the list of all the feedbacks given by various users is listed on the index page
+ @expose('jinja:forgefeedback:templates/feedback/index.html')
+ def get_review_list(self, **kw):
+ self.review_list = Feedback.query.find(dict(project_id=c.project._id)).sort('created_date', pymongo.DESCENDING).all()
+ return self.review_list
+
+ # The new feedback given by the logged in user which includes the review, rating, project id and the user id are all flushed into the database
+ @require_post()
+ @expose('jinja:forgefeedback:templates/feedback/index.html')
+ def create_feedback(self, description=None, rating=None, **kw):
+ """saving the review for the first time """
+ require_access(c.app, 'create')
+ p = Feedback( description=description , rating=rating, user_id=c.user._id, project_id=c.project._id)
+ session(p).flush()
+ flash('Feedback successfully added')
+ M.main_orm_session.flush()
+ g.director.create_activity(c.user, 'posted', p,related_nodes=[c.project], tags=['description'])
+ return dict(review_list = self.get_review_list(), rating=self.getRating())
+
+ # called on click of the Feedback link
+ @with_trailing_slash
+ @expose('jinja:forgefeedback:templates/feedback/new_feedback.html')
+ def new_feedback(self, **kw):
+ require_access(c.app, 'create')
+ return dict(action=c.app.config.url() + 'create')
+
+ #called on click of edit review link and displays the previous feedback
+ @expose('jinja:forgefeedback:templates/feedback/edit_feedback.html')
+ def edit_feedback(self, **kw):
+ self.review = Feedback.query.find({'reported_by_id' : c.user._id,'project_id' : c.project._id}).first()
+ return dict(description = self.review.description, rating=self.review.rating)
+
+ # The edited feedback will be updated in the index page
+ @expose('jinja:forgefeedback:templates/feedback/index.html')
+ def edit_user_review(self,description=None, rating=None, **kw):
+
+ Feedback.query.update(
+ {'reported_by_id': c.user._id,'project_id' : c.project._id},
+ {'$set': {'description': description, 'rating':rating}})
+ self.rating = Feedback.query.find({'reported_by_id' : c.user._id,'project_id' : c.project._id}).first()
+ flash('Feedback successfully edited')
+ g.director.create_activity(c.user,'modified', self.rating, related_nodes=[c.project], tags=['description'])
+ return dict(review_list = self.get_review_list(), rating=self.getRating())
+
+ #called when user clicks on delete link in feedback page
+ @without_trailing_slash
+ @expose('jinja:forgefeedback:templates/feedback/index.html')
+ def delete_feedback(self, **kw):
+ user_review = Feedback.query.find({'reported_by_id' : c.user._id,'project_id' : c.project._id}).first()
+ Feedback.query.remove(dict({'reported_by_id' : c.user._id,'project_id' : c.project._id}))
+ rating_by_user = Feedback.query.find({
+ 'reported_by_id' : c.user._id,'project_id' : c.project._id}
+ ).count()
+ if (rating_by_user > 0) :
+ user_has_already_reviewed = True
+ else :
+ user_has_already_reviewed = False
+ flash('Feedback successfully deleted')
+ M.main_orm_session.flush()
+ return dict(review_list = self.get_review_list(), user_has_already_reviewed = user_has_already_reviewed, rating=self.getRating())
+
+ #This method is used to check for profanity in feedback text
+ @expose()
+ def feedback_check(self, description=None):
+ pf = ProfanityFilter()
+ if description:
+ result = str(pf.is_profane(description)).lower()
+ else:
+ result = 'None'
+ return result
+
+
+ # This method count the number of stars finds their sum and calculates the average of all the star rating count
+ def getRating(self, **kw):
+
+
+ onestarcount = TM.Feedback.query.find({'rating':'1','project_id':c.project._id}).count()
+ twostarcount = TM.Feedback.query.find({'rating':'2','project_id':c.project._id}).count()
+ threestarcount = TM.Feedback.query.find({'rating':'3','project_id':c.project._id}).count()
+ fourstarcount = TM.Feedback.query.find({'rating':'4','project_id':c.project._id}).count()
+ fivestarcount = TM.Feedback.query.find({'rating':'5','project_id':c.project._id}).count()
+ sum_of_ratings = float(fivestarcount + fourstarcount + threestarcount + twostarcount + onestarcount)
+ if (sum_of_ratings != 0):
+ average_user_ratings =float((5*fivestarcount)+(4*fourstarcount)+(3*threestarcount)+(2*twostarcount)+(1*onestarcount))/sum_of_ratings
+ float_rating = float(average_user_ratings)
+ int_rating = int(float_rating)
+ float_point_value = float_rating - int_rating
+ if(float_point_value < 0.25 ):
+ c.project.rating= int_rating
+ elif(float_point_value>=0.25<0.75):
+ c.project.rating = 0.5 + int_rating
+ elif(float_point_value >= 0.75):
+ c.project.rating=float(int_rating)+1
+ return average_user_ratings
+ if (sum_of_ratings == 0):
+ c.project.rating = 0.0
+
+
+
+
+
+
+
+
diff --git a/ForgeFeedback/forgefeedback/model/__init__.py b/ForgeFeedback/forgefeedback/model/__init__.py
new file mode 100644
index 0000000..19f031b
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/model/__init__.py
@@ -0,0 +1,21 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from feedback import Feedback
+
+
+
diff --git a/ForgeFeedback/forgefeedback/model/feedback.py b/ForgeFeedback/forgefeedback/model/feedback.py
new file mode 100644
index 0000000..2ef7326
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/model/feedback.py
@@ -0,0 +1,82 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import logging
+import urllib
+# Non-stdlib imports
+
+from datetime import datetime
+from bson import ObjectId
+from tg import tmpl_context as c
+from ming import schema
+from ming.orm import Mapper
+from ming.orm import FieldProperty, ForeignIdProperty, RelationProperty
+
+# Pyforge-specific imports
+
+from allura.model.artifact import VersionedArtifact
+from allura.model.auth import AlluraUserProperty, User
+from allura.model.project import ProjectRole
+from allura.model.timeline import ActivityObject
+from allura.lib import helpers as h
+
+log = logging.getLogger(__name__)
+
+
+class Feedback(VersionedArtifact, ActivityObject):
+
+ class __mongometa__:
+ name = 'feedback'
+
+ type_s = 'Feedback'
+ _id = FieldProperty(schema.ObjectId)
+ created_date = FieldProperty(datetime, if_missing=datetime.utcnow)
+ rating = FieldProperty(str, if_missing='')
+ description = FieldProperty(str, if_missing='')
+ reported_by_id = AlluraUserProperty(if_missing=lambda: c.user._id)
+ project_id = ForeignIdProperty('Project', if_missing=lambda: c.project._id)
+ reported_by = RelationProperty(User, via='reported_by_id')
+
+ def index(self):
+ result = VersionedArtifact.index(self)
+ result.update(
+ created_date_dt=self.created_date,
+ text=self.description,
+ )
+ return result
+
+ @property
+ def activity_name(self):
+ return 'a review comment'
+
+ @property
+ def activity_extras(self):
+ d = ActivityObject.activity_extras.fget(self)
+ d.update(summary=self.description)
+ return d
+ def url(self):
+ return self.app_config.url()
+
+Mapper.compile_all()
+
+
+
+
+
+
+
+
diff --git a/ForgeFeedback/forgefeedback/nf/feedback/css/feedback.css b/ForgeFeedback/forgefeedback/nf/feedback/css/feedback.css
new file mode 100644
index 0000000..15c2f9c
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/nf/feedback/css/feedback.css
@@ -0,0 +1,112 @@
+.rating {
+ float:left;
+}
+
+/* :not(:checked) is a filter, so that browsers that dont support :checked dont
+ follow these rules. Every browser that supports :checked also supports :not(), so
+ it doesnt make the test unnecessarily selective */
+.rating:not(:checked) > input {
+ position:absolute;
+ top:-9999px;
+ clip:rect(0,0,0,0);
+}
+.rating:not(:checked) > label {
+ float:right;
+ width:1em;
+ padding:0 .1em;
+ overflow:hidden;
+ white-space:nowrap;
+ cursor:pointer;
+ font-size:200%;
+ line-height:1.2;
+ color:#ddd;
+ text-shadow:1px 1px #bbb, 2px 2px #666, .1em .1em .2em rgba(0,0,0,.5);
+}
+.rating:not(:checked) > label:before {
+ content: '★ ';
+}
+.rating > input:checked ~ label {
+ color: #f70;
+ text-shadow:1px 1px #c60, 2px 2px #940, .1em .1em .2em rgba(0,0,0,.5);
+}
+
+.rating:not(:checked) > label:hover,
+.rating:not(:checked) > label:hover ~ label {
+ color: gold;
+ text-shadow:1px 1px goldenrod, 2px 2px #B57340, .1em .1em .2em rgba(0,0,0,.5);
+}
+.rating > input:checked + label:hover,
+.rating > input:checked + label:hover ~ label,
+.rating > input:checked ~ label:hover,
+.rating > input:checked ~ label:hover ~ label,
+.rating > label:hover ~ input:checked ~ label {
+ color: #ea0;
+ text-shadow:1px 1px goldenrod, 2px 2px #B57340, .1em .1em .2em rgba(0,0,0,.5);
+}
+.rating > label:active {
+ position:relative;
+ top:2px;
+ left:2px;
+}
+.feed {
+ padding: 0 20px 20px 20px;
+}
+.feed, .feed * {
+ box-sizing: border-box;
+}
+.feed form textarea {
+ width: 100%;
+ height: 200px;
+ display: block;
+}
+.index {
+ padding: 0 20px 20px 20px;
+}
+.index ul.list {
+ list-style: none;
+ margin: 0;
+}
+.index ul.list li {
+ padding: 20px;
+ border-bottom: 1px solid #eee;
+}
+.index ul.list li time {
+ font-size: 12px;
+ color: #777;
+}
+.index ul.list li h1 {
+ padding: 0;
+ font-size: 16px;
+ line-height: 32px;
+}
+.index ul.list li p {
+ padding: 0;
+ vertical-align: top;
+ font-size: 14px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.index ul.list li .avatar {
+ float: left;
+ margin: 10px 10px 0 0;
+}
+.fa-star {
+color:orange;
+}
+.fa-star-o {
+color :orange;
+}
+.rating {
+position:relative;
+}
+p {
+position:absolute;
+}
+.grid-19{
+ width:885px !important;
+}
+.btn.link.cancel_feed{
+ float:right;
+}
+
diff --git a/ForgeFeedback/forgefeedback/templates/__init__.py b/ForgeFeedback/forgefeedback/templates/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ForgeFeedback/forgefeedback/templates/feedback/common_feedback.html b/ForgeFeedback/forgefeedback/templates/feedback/common_feedback.html
new file mode 100644
index 0000000..48db583
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/templates/feedback/common_feedback.html
@@ -0,0 +1,87 @@
+{#-
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+This is used when user is trying to imput a new review
+
+-#}
+
+
+<!-- macro for feedback textarea -->
+{% macro feed_textarea(name='description',id='description',placeholder='',description='') %}
+<textarea class="textbox" name="{{name}}" id="{{id}}" maxlength=100 onkeyup="manage()" placeholder="{{placeholder}}">{{description}}</textarea>
+{% endmacro %}
+
+<!-- macro for feedback alert message -->
+{% macro alert_message() %}
+ <div id="check">
+ <p id="status_msg" style="color:#f33;"> <i class="fa fa-exclamation-triangle"></i> Profanity alert! Please update your feedback.</p>
+ </div>
+{% endmacro %}
+
+<!-- macro for feedback cancel button -->
+{% macro feed_cancel(url='') %}
+ <a href="{{url}}" class="btn link cancel_feed" >Cancel</a>
+{% endmacro %}
+
+<!-- macro for profanity scripts -->
+{% macro profanity_scripts(url='') %}
+<script type="text/javascript">
+function manage()
+{
+ var description = document.getElementById('description');
+ var star5 = $('#star5').is(':checked');
+ var star4 = $('#star4').is(':checked');
+ var star3 = $('#star3').is(':checked');
+ var star2 = $('#star2').is(':checked');
+ var star1 = $('#star1').is(':checked');
+ var bt = document.getElementById('button');
+ if (description.value.trim().length != 0 && (star5 || star4 || star3 || star2 || star1 )) {
+ bt.disabled = false;
+ }
+ else {
+ bt.disabled = true;
+ }
+}
+
+</script>
+
+<script type="text/javascript">
+$('#status_msg').hide();
+
+$("#description").keyup(function(){
+ var description = $("#description").val();
+ $.ajax({
+ url:'{{url}}feedback_check',
+ type:'GET',
+ data: {'description':description},
+ success: function(status){
+ if (status == 'true'){
+ $('#button').attr('disabled', true);
+ $('#status_msg').show();
+ }
+ else{
+ $('#status_msg').hide();
+ }
+ }
+ });
+});
+</script>
+{% endmacro %}
+
+
+
+
diff --git a/ForgeFeedback/forgefeedback/templates/feedback/edit_feedback.html b/ForgeFeedback/forgefeedback/templates/feedback/edit_feedback.html
new file mode 100644
index 0000000..8f11e2a
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/templates/feedback/edit_feedback.html
@@ -0,0 +1,103 @@
+{#-
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+This is used when user is trying to imput a new review
+
+-#}
+
+{% import 'allura:templates/jinja_master/lib.html' as lib with context %}
+
+{% import 'forgefeedback:templates/feedback/common_feedback.html' as common_feed %}
+
+{% extends g.theme.master %}
+{% set hide_left_bar = True %}
+{% do g.register_app_css('css/feedback.css') %}
+
+{% block title %}{{c.project.name}} / {{c.app.config.options.mount_label}} / Edit feedback {% endblock %}
+
+{% block header %}{{c.app.config.options.mount_label}}{% endblock %}
+
+{% block content %}
+<style>
+p {
+ position:inherit;
+}
+</style>
+
+<div class="feed">
+<form action="{{ c.app.url }}edit_user_review" method="post">
+
+<div class="row">
+<div class="col-25">
+
+ <p> <h2>Edit your feedback for <b> {{c.project.name}} </b></h2> </p>
+ {{ common_feed.alert_message() }}
+
+ </div>
+ <div class="col-75">
+<fieldset class="rating">
+ {% if rating == '5' %}
+ <input type="radio" id="star5" name="rating" value="5" checked="checked" onclick="manage()" /><label for="star5" title="Excellent!"></label>
+ {% else %}
+ <input type="radio" id="star5" name="rating" value="5" onclick="manage()" /><label for="star5" title="Excellent!"></label>
+ {% endif %}
+ {% if rating == '4' %}
+ <input type="radio" id="star4" name="rating" value="4" checked="checked" onclick="manage()" /><label for="star4" title="Great!"></label>
+ {% else %}
+ <input type="radio" id="star4" name="rating" value="4" onclick="manage()" /><label for="star4" title="Great!"></label>
+ {% endif %}
+ {% if rating == '3' %}
+ <input type="radio" id="star3" name="rating" value="3" checked="checked" onclick="manage()" /><label for="star3" title="Good!"></label>
+ {% else %}
+ <input type="radio" id="star3" name="rating" value="3" onclick="manage()" /><label for="star3" title="Good!"></label>
+ {% endif %}
+ {% if rating == '2' %}
+ <input type="radio" id="star2" name="rating" value="2" checked="checked" onclick="manage()" /><label for="star2" title="Average"></label>
+ {% else %}
+ <input type="radio" id="star5" name="rating" value="5" onclick="manage()" /><label for="star5" title="Excellent!"></label>
+ {% endif %}
+ {% if rating == '1' %}
+ <input type="radio" id="star1" name="rating" value="1" checked="checked" onclick="manage()" /><label for="star1" title="Poor"></label>
+ {% else %}
+ <input type="radio" id="star5" name="rating" value="5" onclick="manage()" /><label for="star5" title="Excellent!"></label>
+ {% endif %}
+
+</fieldset>
+</div>
+</div>
+
+<!-- feedback textarea's macro -->
+{{ common_feed.feed_textarea(placeholder='Edit your feedback here',description=description ) }}
+
+<div class="grid-19">
+ <input type="submit" value="Submit" id="button" >
+ {{ common_feed.feed_cancel(url=c.app.url) }}
+</div>
+
+{{ lib.csrf_token() }}
+
+</form>
+</div>
+
+{% endblock %}
+
+{% block extra_js %}
+<!-- profanity script's macro -->
+ {{ common_feed.profanity_scripts(url=c.app.url) }}
+{% endblock %}
+
+
diff --git a/ForgeFeedback/forgefeedback/templates/feedback/feedback_list.html b/ForgeFeedback/forgefeedback/templates/feedback/feedback_list.html
new file mode 100644
index 0000000..195fb2e
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/templates/feedback/feedback_list.html
@@ -0,0 +1,100 @@
+{#-
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-#}
+
+{% import 'forgeactivity:templates/macros.html' as am with context %}
+{#- to add the time -#}
+{% from 'allura:templates/jinja_master/lib.html' import abbr_date with context %}
+{% do g.register_app_css('css/feedback.css') %}
+{% block content %}
+
+{% for timeline in review_list %}
+<li>
+ <time>
+ {{abbr_date(timeline.created_date)}}
+ </time>
+<br/>
+ <h1>
+ {{ am.icon (timeline.reported_by, 32, 'avatar') }}
+ {{am.activity_obj(timeline.reported_by)}}
+ </h1><br/>
+ <fieldset class="rating" style="float:left">
+
+ {% if timeline.rating =='1' %}
+ <span class="fa fa-star checked"></span>
+ <span class="fa fa-star-o "></span>
+ <span class="fa fa-star-o "></span>
+ <span class="fa fa-star-o "></span>
+ <span class="fa fa-star-o"></span>
+
+ {% endif %}
+
+ {% if timeline.rating =='2' %}
+ <span class="fa fa-star checked"></span>
+ <span class="fa fa-star checked"></span>
+ <span class="fa fa-star-o"></span>
+ <span class="fa fa-star-o"></span>
+ <span class="fa fa-star-o"></span>
+
+ {% endif %}
+
+ {% if timeline.rating =='3' %}
+ <span class="fa fa-star checked"></span>
+ <span class="fa fa-star checked"></span>
+ <span class="fa fa-star checked"></span>
+ <span class="fa fa-star-o"></span>
+ <span class="fa fa-star-o"></span>
+ {% endif %}
+
+ {% if timeline.rating =='4' %}
+ <span class="fa fa-star checked"></span>
+ <span class="fa fa-star checked"></span>
+ <span class="fa fa-star checked"></span>
+ <span class="fa fa-star checked"></span>
+ <span class="fa fa-star-o"></span>
+
+ {% endif %}
+
+ {% if timeline.rating =='5' %}
+ <span class="fa fa-star checked"></span>
+ <span class="fa fa-star checked"></span>
+ <span class="fa fa-star checked"></span>
+ <span class="fa fa-star checked"></span>
+ <span class="fa fa-star checked"></span>
+ {% endif %}
+ </fieldset>
+<br/>
+
+ {{timeline.description}}
+
+</li>
+{% endfor %}
+{% endblock %}
+
+{% block extra_css %}
+<style>
+.rating {
+position:relative;
+}
+p {
+position:absolute;
+}
+</style>
+{% endblock %}
+
+
diff --git a/ForgeFeedback/forgefeedback/templates/feedback/index.html b/ForgeFeedback/forgefeedback/templates/feedback/index.html
new file mode 100644
index 0000000..fe96856
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/templates/feedback/index.html
@@ -0,0 +1,56 @@
+{#-
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ e Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-#}
+{% extends g.theme.master %}
+{% do g.register_app_css('css/feedback.css') %}
+{% set hide_left_bar = True %}
+{% block nav_menu %}
+{{super()}}
+<br/>
+<br/>
+{% endblock %}
+
+{% block title %}{{c.project.name}} / {{c.app.config.options.mount_label}} / Feedback{% endblock %}
+{% block actions %}
+{% endblock %}
+
+{% block header %}{{c.app.config.options.mount_label}}{% endblock %}
+
+{% block content %}
+
+{% if c.user and c.user != c.user.anonymous() %}
+<div class="index">
+ {% if user_has_already_reviewed == False %}
+ <p><h2> Provide your <a href="{{c.app.url}}new_feedback">Feedback</a> for {{c.project.name}} </h2></p>
+ {% else %}
+ <p><h2><a href="{{c.app.url}}edit_feedback"> Edit </a> or <a href="{{c.app.url}}delete_feedback"> Delete </a> your feedback for {{c.project.name}} </h2> </p>
+ {% endif %}
+</div>
+{% else %}
+<p><h3> <a target="_parent" href="{{ config.get('auth.login_url', '/auth/') }}">Login </a> to Add/Modify Feedback</h3></p>
+{% endif %}
+<hr/>
+<div class ="index" >
+ <ul class="list">
+ {% include 'forgefeedback:templates/feedback/feedback_list.html' %}
+ </ul>
+</div>
+{% endblock %}
+
+
+
diff --git a/ForgeFeedback/forgefeedback/templates/feedback/new_feedback.html b/ForgeFeedback/forgefeedback/templates/feedback/new_feedback.html
new file mode 100644
index 0000000..b472aab
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/templates/feedback/new_feedback.html
@@ -0,0 +1,79 @@
+{#-
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+This is used when user is trying to imput a new review
+
+-#}
+{% import 'allura:templates/jinja_master/lib.html' as lib with context %}
+
+{% import 'forgefeedback:templates/feedback/common_feedback.html' as common_feed %}
+
+{% extends g.theme.master %}
+{% set hide_left_bar = True %}
+{% do g.register_app_css('css/feedback.css') %}
+{% block title %}{{c.project.name}} / {{c.app.config.options.mount_label}} / New feedback{% endblock %}
+
+{% block header %}{{c.app.config.options.mount_label}}{% endblock %}
+
+{% block content %}
+<style>
+p {
+ position:inherit;
+}
+</style>
+
+<div class="feed">
+ <form action="{{ c.app.url }}create_feedback" method="post">
+
+ <div class="row">
+ <div class="col-25">
+
+ <p> <h2>Provide your feedback for <b> {{c.project.name}}</b></h2> </p>
+ {{ common_feed.alert_message() }}
+
+ </div>
+ <div class="col-75">
+ <fieldset class="rating" id="sar">
+ <input type="radio" id="star5" name="rating" value="5" onclick="manage()" /><label for="star5" title="Excellent">5 stars</label>
+ <input type="radio" id="star4" name="rating" value="4" onclick="manage()" /><label for="star4" title="Great">4 stars</label>
+ <input type="radio" id="star3" name="rating" value="3" onclick="manage()" /><label for="star3" title="Good">3 stars</label>
+ <input type="radio" id="star2" name="rating" value="2" onclick="manage()" /><label for="star2" title="Average">2 stars</label>
+ <input type="radio" id="star1" name="rating" value="1" onclick="manage()" /><label for="star1" title="Poor">1 star</label>
+ </fieldset>
+ </div>
+ </div>
+
+ <!-- feedback textarea's macro -->
+ {{ common_feed.feed_textarea(placeholder='Enter your feedback here',description=description ) }}
+ <div class="grid-19">
+ <input type="submit" value="Submit" id="button" disabled />
+ {{ common_feed.feed_cancel(url=c.app.url) }}
+ </div>
+ {{ lib.csrf_token() }}
+ </form>
+</div>
+{% endblock %}
+
+
+{% block extra_js %}
+<!-- profanity script's macro -->
+ {{ common_feed.profanity_scripts(url=c.app.url) }}
+{% endblock %}
+
+
+
+
diff --git a/ForgeFeedback/forgefeedback/tests/__init__.py b/ForgeFeedback/forgefeedback/tests/__init__.py
new file mode 100644
index 0000000..77505f1
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/tests/__init__.py
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
diff --git a/ForgeFeedback/forgefeedback/tests/functional/__init__.py b/ForgeFeedback/forgefeedback/tests/functional/__init__.py
new file mode 100644
index 0000000..77505f1
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/tests/functional/__init__.py
@@ -0,0 +1,17 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
diff --git a/ForgeFeedback/forgefeedback/tests/functional/test_root.py b/ForgeFeedback/forgefeedback/tests/functional/test_root.py
new file mode 100644
index 0000000..b97eafa
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/tests/functional/test_root.py
@@ -0,0 +1,69 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from tg import tmpl_context as c
+from tg import config
+
+from nose.tools import assert_equal, assert_in, assert_not_in, assert_true, assert_false, assert_raises
+
+from allura import model as M
+from alluratest.controller import TestController
+from allura.lib import helpers as h
+from allura.tests import decorators as td
+
+from forgefeedback import model as FM
+
+class TestFeedback(TestController):
+ def setUp(self):
+ TestController.setUp(self)
+
+ def test_feedback(self):
+ c.user = M.User.by_username('test-admin')
+ self.app.get('/feedback/')
+ r = self.app.get('/p/test/feedback')
+ assert 'test' in r
+ assert_in('<a href="/p/test/feedback/new_feedback">Feedback</a>', r)
+
+ def test_new_feedback(self):
+ c.user = M.User.by_username('test-admin')
+ self.app.get('/feedback/')
+ r = self.app.get('/p/test/feedback/new_feedback/')
+ assert_in('Provide your feedback for <b> Test Project</b>',r)
+ assert_in('Enter your feedback here', r)
+
+ def test_create_feedback(self):
+ resp = post_feedback(self)
+ assert_in('Good tool',resp)
+
+ def test_edit_feedback(self):
+ post_feedback(self)
+ data = {'rating': '2','description':'Not useful'}
+ resp = self.app.post('/p/test/feedback/edit_user_review',data)
+ assert_in('Not useful',resp)
+
+ def test_delete_feedback(self):
+ post_feedback(self)
+ resp=self.app.get('/p/test/feedback/delete_feedback')
+ assert_in('<a href="/p/test/feedback/new_feedback">Feedback</a>',resp)
+
+
+def post_feedback(self):
+ c.user =M.User.by_username('test-admin')
+ self.app.get('/feedback/')
+ self.app.get('/p/test/feedback')
+ data = {'rating':'4','description':'Good tool'}
+ resp = self.app.post('/p/test/feedback/create_feedback/',data)
+ return resp
diff --git a/ForgeFeedback/forgefeedback/tests/test_feedback_roles.py b/ForgeFeedback/forgefeedback/tests/test_feedback_roles.py
new file mode 100644
index 0000000..bd7f902
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/tests/test_feedback_roles.py
@@ -0,0 +1,54 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from tg import tmpl_context as c, app_globals as g
+
+from nose.tools import assert_equal
+
+from alluratest.controller import setup_basic_test, setup_global_objects
+from allura import model as M
+from allura.lib import security
+from allura.tests import decorators as td
+from allura.lib import helpers as h
+
+def setUp():
+ setup_basic_test()
+ setup_with_tools()
+
+
+@td.with_tool('u/test-user-1','feedback')
+@td.with_user_project('test-user-1')
+def setup_with_tools():
+ setup_global_objects()
+ h.set_context('test', neighborhood='Projects')
+ c.project.install_app('feedback', 'feedback')
+ g.set_app('feedback')
+
+
+def test_role_assignments():
+ admin = M.User.by_username('test-admin')
+ user = M.User.by_username('test-user')
+ anon = M.User.anonymous()
+
+ def check_access(perm):
+ pred = security.has_access(c.app, perm)
+ return pred(user=admin), pred(user=user), pred(user=anon)
+ assert_equal(check_access('read'), (True, True, True))
+ assert_equal(check_access('create'), (True, True, False))
+ assert_equal(check_access('update'), (True, False, False))
+ assert_equal(check_access('delete'), (True, False, False))
+
diff --git a/ForgeFeedback/forgefeedback/tests/unit/__init__.py b/ForgeFeedback/forgefeedback/tests/unit/__init__.py
new file mode 100644
index 0000000..9c6667a
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/tests/unit/__init__.py
@@ -0,0 +1,51 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from tg import tmpl_context as c
+from ming.orm.ormsession import ThreadLocalORMSession
+
+from allura.websetup import bootstrap
+from allura.lib import helpers as h
+from allura.lib import plugin
+from allura import model as M
+from alluratest.controller import setup_basic_test
+
+
+def setUp():
+ setup_basic_test()
+
+
+class FeedbackTestWithModel(object):
+
+ def setUp(self):
+ bootstrap.wipe_database()
+ project_reg = plugin.ProjectRegistrationProvider.get()
+ c.user = bootstrap.create_user('Test User')
+ neighborhood = M.Neighborhood(name='Projects', url_prefix='/p/',
+ features=dict(private_projects=False,
+ max_projects=None,
+ css='none',
+ google_analytics=False))
+ project_reg.register_neighborhood_project(neighborhood, [c.user])
+ c.project = neighborhood.register_project('test', c.user)
+ c.project.install_app('Feedback', 'feedback')
+ ThreadLocalORMSession.flush_all()
+ h.set_context('test', 'feedback', neighborhood='Projects')
+
+ def tearDown(self):
+ ThreadLocalORMSession.close_all()
+
diff --git a/ForgeFeedback/forgefeedback/tests/unit/test_feedback.py b/ForgeFeedback/forgefeedback/tests/unit/test_feedback.py
new file mode 100644
index 0000000..604041e
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/tests/unit/test_feedback.py
@@ -0,0 +1,41 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from datetime import datetime
+from nose.tools import assert_equal, assert_true
+from tg import tmpl_context as c
+
+from forgefeedback.tests.unit import FeedbackTestWithModel
+from forgefeedback import model as M
+
+class TestFeedback(FeedbackTestWithModel):
+
+ def test_feedback(self):
+ feedback = M.Feedback()
+ feedback.rating='4'
+ feedback.description='Very good tool'
+ assert_equal(feedback.rating, '4')
+ assert_equal(feedback.description, 'Very good tool')
+ assert_equal(feedback.activity_extras['summary'], feedback.description)
+ assert_true('allura_id' in feedback.activity_extras)
+
+ def test_index(self):
+ feedback= M.Feedback()
+ feedback.rating= '4'
+ feedback.description ='Good tool'
+ result= feedback.index()
+ assert_equal(result["text"], 'Good tool')
diff --git a/ForgeFeedback/forgefeedback/tests/unit/test_root_controller.py b/ForgeFeedback/forgefeedback/tests/unit/test_root_controller.py
new file mode 100644
index 0000000..f99332c
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/tests/unit/test_root_controller.py
@@ -0,0 +1,80 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import unittest
+
+from mock import Mock, patch
+from ming.orm.ormsession import session
+from tg import tmpl_context as c
+from tg import request
+from nose.tools import assert_equal
+
+from allura.lib import helpers as h
+from allura.model import User
+
+from forgefeedback.tests.unit import FeedbackTestWithModel
+from forgefeedback.model import Feedback
+from forgefeedback import feedback_main
+
+class TestFeedbackApp(FeedbackTestWithModel):
+
+ def setUp(self):
+ super(TestFeedbackApp, self).setUp()
+ c.user = User(username='test-user')
+ h.set_context('test', 'feedback', neighborhood='Projects')
+
+ def test_index(self):
+ reviews = feedback_main.RootController().index()
+ assert reviews['user_has_already_reviewed']==False
+ create_feedbacks()
+ reviews = feedback_main.RootController().index()
+ assert reviews['user_has_already_reviewed']==True
+
+ def test_feedback(self):
+ create_feedbacks()
+ reviews = feedback_main.RootController().get_review_list()
+ assert reviews[0].description =='Very good tool'
+ assert reviews[1].description =='Not Useful'
+ assert reviews[0].rating =='5'
+ assert reviews[1].rating =='2'
+
+ def test_getRating(self):
+ create_feedbacks()
+ rating=feedback_main.RootController().getRating()
+ assert_equal(rating,3.5)
+
+ def test_edit_feedback(self):
+ create_feedbacks()
+ old_feedback= feedback_main.RootController().edit_feedback()
+ assert old_feedback['description'] =='Very good tool'
+
+ def test_check_feedback(self):
+ feed_check = feedback_main.RootController().feedback_check('good')
+ assert feed_check == 'false'
+ feed_check = feedback_main.RootController().feedback_check('shit')
+ assert feed_check == 'true'
+
+def create_feedbacks():
+ feedback_1= create_feedback('2', 'Not Useful')
+ c.user = User(username='test-admin')
+ h.set_context('test', 'feedback', neighborhood='Projects')
+ feedback_2 = create_feedback('5', 'Very good tool')
+
+def create_feedback(rating, description):
+ feedback = Feedback(rating=rating, description=description)
+ session(feedback).flush()
+ return feedback
diff --git a/ForgeFeedback/forgefeedback/version.py b/ForgeFeedback/forgefeedback/version.py
new file mode 100644
index 0000000..1333217
--- /dev/null
+++ b/ForgeFeedback/forgefeedback/version.py
@@ -0,0 +1,24 @@
+#Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+__version_info__ = (0, 0)
+__version__ = '.'.join(map(str, __version_info__))
+
+
+
+
+
diff --git a/ForgeFeedback/setup.py b/ForgeFeedback/setup.py
new file mode 100644
index 0000000..cce91c5
--- /dev/null
+++ b/ForgeFeedback/setup.py
@@ -0,0 +1,53 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+
+from setuptools import setup, find_packages
+
+from forgefeedback.version import __version__
+
+setup(name='ForgeFeedback',
+ version=__version__,
+ description="",
+ long_description="""\
+""",
+ # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
+ classifiers=[],
+ keywords='',
+ author='',
+ author_email='',
+ url='',
+ license='',
+ packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
+ include_package_data=True,
+ zip_safe=False,
+ install_requires=[
+ # -*- Extra requirements: -*-
+ 'Allura',
+ ],
+ entry_points="""
+ # -*- Entry points: -*-
+ [allura]
+ Feedback=forgefeedback.feedback_main:ForgeFeedbackApp
+
+
+ """,
+ )
+
+
+
+
diff --git a/requirements.in b/requirements.in
index 35e0f5b..a8276a7 100644
--- a/requirements.in
+++ b/requirements.in
@@ -25,6 +25,8 @@ Paste
PasteDeploy
PasteScript
Pillow
+# profanity filter for feedback
+profanityfilter==2.0.6
Pygments
pymongo==2.8.1
Pypeline[creole,markdown,textile,rst]
@@ -57,4 +59,4 @@ testfixtures
WebTest==2.0.33
# deployment
-gunicorn==19.4.5
\ No newline at end of file
+gunicorn==19.4.5
diff --git a/requirements.txt b/requirements.txt
index 7d4c669..91b2080 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -40,6 +40,7 @@ gunicorn==19.4.5
html5lib==1.0.1
httplib2==0.13.1 # via oauth2
idna==2.8 # via requests
+inflection==0.3.1 # via profanityfilter
ipaddress==1.0.22 # via cryptography
ipython-genutils==0.2.0 # via traitlets
ipython==5.8.0
@@ -64,6 +65,7 @@ pexpect==4.7.0 # via ipython
pickleshare==0.7.5 # via ipython
pillow==6.1.0
poster==0.8.1
+profanityfilter==2.0.6
prompt-toolkit==1.0.16 # via ipython
ptyprocess==0.6.0 # via pexpect
pycparser==2.19 # via cffi