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:08 UTC

[allura] branch master updated (ea34e18 -> 7f41e15)

This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git.


    from ea34e18  [#8345] default all event tasks created within web requests & tasks, to not flush immediately, so that they flush at the end of the task/req execution in MingMiddleware with everything else
     new 66a2949  [FEATURE] Added ForgeFeedback app
     new a6afcf9  Added Apache license header
     new 59144b6  Handled review comments
     new 7f41e15  More polishing

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 Allura/allura/model/project.py                     |   1 +
 Allura/allura/templates/jinja_master/nav_menu.html |  32 +++
 .../forgefeedback}/__init__.py                     |   0
 ForgeFeedback/forgefeedback/feedback_main.py       | 250 +++++++++++++++++++++
 .../forgefeedback/model}/__init__.py               |   4 +-
 ForgeFeedback/forgefeedback/model/feedback.py      |  80 +++++++
 .../forgefeedback/nf/feedback/css/feedback.css     | 131 +++++++++++
 .../forgefeedback/templates}/__init__.py           |   0
 .../templates/feedback/common_feedback.html        |  92 ++++++++
 .../templates/feedback/edit_feedback.html          |  84 +++++++
 .../templates/feedback/feedback_list.html          | 100 +++++++++
 .../forgefeedback/templates/feedback/index.html    |  69 ++++++
 .../templates/feedback/new_feedback.html           |  79 +++++++
 .../forgefeedback/tests}/__init__.py               |   0
 .../forgefeedback/tests/functional}/__init__.py    |   0
 .../forgefeedback/tests/functional/test_root.py    |  72 ++++++
 .../forgefeedback/tests/test_feedback_roles.py     |  17 +-
 .../forgefeedback}/tests/unit/__init__.py          |   6 +-
 .../forgefeedback/tests/unit/test_feedback.py      |  48 ++--
 .../tests/unit/test_root_controller.py             |  83 +++++++
 .../forgefeedback}/version.py                      |   2 +-
 {ForgeLink => ForgeFeedback}/setup.py              |  13 +-
 requirements.in                                    |   4 +-
 requirements.txt                                   |   2 +
 24 files changed, 1122 insertions(+), 47 deletions(-)
 copy {ForgeWiki/forgewiki/widgets => ForgeFeedback/forgefeedback}/__init__.py (100%)
 mode change 100644 => 100755
 create mode 100755 ForgeFeedback/forgefeedback/feedback_main.py
 copy {Allura/allura/lib => ForgeFeedback/forgefeedback/model}/__init__.py (96%)
 mode change 100644 => 100755
 create mode 100755 ForgeFeedback/forgefeedback/model/feedback.py
 create mode 100755 ForgeFeedback/forgefeedback/nf/feedback/css/feedback.css
 copy {ForgeWiki/forgewiki/widgets => ForgeFeedback/forgefeedback/templates}/__init__.py (100%)
 mode change 100644 => 100755
 create mode 100755 ForgeFeedback/forgefeedback/templates/feedback/common_feedback.html
 create mode 100755 ForgeFeedback/forgefeedback/templates/feedback/edit_feedback.html
 create mode 100755 ForgeFeedback/forgefeedback/templates/feedback/feedback_list.html
 create mode 100755 ForgeFeedback/forgefeedback/templates/feedback/index.html
 create mode 100755 ForgeFeedback/forgefeedback/templates/feedback/new_feedback.html
 copy {ForgeWiki/forgewiki/widgets => ForgeFeedback/forgefeedback/tests}/__init__.py (100%)
 mode change 100644 => 100755
 copy {ForgeWiki/forgewiki/widgets => ForgeFeedback/forgefeedback/tests/functional}/__init__.py (100%)
 mode change 100644 => 100755
 create mode 100755 ForgeFeedback/forgefeedback/tests/functional/test_root.py
 copy ForgeWiki/forgewiki/tests/test_wiki_roles.py => ForgeFeedback/forgefeedback/tests/test_feedback_roles.py (77%)
 mode change 100644 => 100755
 copy {ForgeBlog/forgeblog => ForgeFeedback/forgefeedback}/tests/unit/__init__.py (92%)
 mode change 100644 => 100755
 copy Dockerfile => ForgeFeedback/forgefeedback/tests/unit/test_feedback.py (53%)
 mode change 100644 => 100755
 create mode 100755 ForgeFeedback/forgefeedback/tests/unit/test_root_controller.py
 copy {ForgeGit/forgegit => ForgeFeedback/forgefeedback}/version.py (92%)
 mode change 100644 => 100755
 copy {ForgeLink => ForgeFeedback}/setup.py (91%)
 mode change 100644 => 100755


[allura] 02/04: Added Apache license header

Posted by br...@apache.org.
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 a6afcf94d9752707bc2464d979b2ce6c37f3015b
Author: Vrinda A <Vr...@in.bosch.com>
AuthorDate: Sat Dec 14 11:50:36 2019 +0530

    Added Apache license header
---
 .../forgefeedback/nf/feedback/css/feedback.css        | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/ForgeFeedback/forgefeedback/nf/feedback/css/feedback.css b/ForgeFeedback/forgefeedback/nf/feedback/css/feedback.css
index 15c2f9c..f6bdedd 100644
--- a/ForgeFeedback/forgefeedback/nf/feedback/css/feedback.css
+++ b/ForgeFeedback/forgefeedback/nf/feedback/css/feedback.css
@@ -1,3 +1,22 @@
+/*
+       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.
+*/
+
 .rating {
     float:left;
 }


[allura] 01/04: [FEATURE] Added ForgeFeedback app

Posted by br...@apache.org.
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>&nbsp;');
+
+  // 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>&nbsp;');
+
+  // 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>&nbsp;');
+
+  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


[allura] 03/04: Handled review comments

Posted by br...@apache.org.
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 59144b629c5969346d95dd16d5879cbeaf3cd44e
Author: Vrinda A <vr...@in.bosch.com>
AuthorDate: Mon Jan 6 14:40:27 2020 +0530

    Handled review comments
---
 ForgeFeedback/forgefeedback/__init__.py            |   0
 ForgeFeedback/forgefeedback/feedback_main.py       | 180 ++++++++++++---------
 ForgeFeedback/forgefeedback/model/__init__.py      |   3 -
 ForgeFeedback/forgefeedback/model/feedback.py      |  26 ++-
 .../forgefeedback/nf/feedback/css/feedback.css     |   0
 ForgeFeedback/forgefeedback/templates/__init__.py  |   0
 .../templates/feedback/common_feedback.html        |  47 +++---
 .../templates/feedback/edit_feedback.html          |  35 +---
 .../templates/feedback/feedback_list.html          |   0
 .../forgefeedback/templates/feedback/index.html    |  19 ++-
 .../templates/feedback/new_feedback.html           |   4 +-
 ForgeFeedback/forgefeedback/tests/__init__.py      |   1 -
 .../forgefeedback/tests/functional/__init__.py     |   1 -
 .../forgefeedback/tests/functional/test_root.py    |  75 ++++-----
 .../forgefeedback/tests/test_feedback_roles.py     |   4 +-
 ForgeFeedback/forgefeedback/tests/unit/__init__.py |   1 -
 .../forgefeedback/tests/unit/test_feedback.py      |  31 ++--
 .../tests/unit/test_root_controller.py             |  39 ++---
 ForgeFeedback/forgefeedback/version.py             |   7 +-
 ForgeFeedback/setup.py                             |   0
 20 files changed, 241 insertions(+), 232 deletions(-)

diff --git a/ForgeFeedback/forgefeedback/__init__.py b/ForgeFeedback/forgefeedback/__init__.py
old mode 100644
new mode 100755
diff --git a/ForgeFeedback/forgefeedback/feedback_main.py b/ForgeFeedback/forgefeedback/feedback_main.py
old mode 100644
new mode 100755
index 0f9a555..4f04b61
--- a/ForgeFeedback/forgefeedback/feedback_main.py
+++ b/ForgeFeedback/forgefeedback/feedback_main.py
@@ -1,4 +1,4 @@
-#	Licensed to the Apache Software Foundation (ASF) under one
+#       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
@@ -26,7 +26,7 @@ from tg import tmpl_context as c, app_globals as g
 from ming.odm import session
 from ming.orm import session
 
-## profanityfilter package ##
+# profanityfilter package
 from profanityfilter import ProfanityFilter
 
 # Pyforge-specific imports
@@ -35,7 +35,7 @@ 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
@@ -53,12 +53,11 @@ log = logging.getLogger(__name__)
 
 class ForgeFeedbackApp(Application):
 
-
     __version__ = version.__version__
     permissions = [
-        'read', 'update', 'create', 
+        'read', 'update', 'create',
         'post', 'admin', 'delete'
-        ]
+    ]
 
     permissions_desc = {
         'read': 'View ratings.',
@@ -72,15 +71,17 @@ class ForgeFeedbackApp(Application):
     ]
     tool_label = 'Feedback'
     tool_description = """
-        Feedbacks are given for the tools in the form of reviews and ratings, edit and delete the feedback"""
+        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
+    max_instances = 1
 
     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)
@@ -110,82 +111,101 @@ class ForgeFeedbackApp(Application):
 
 class RootController(BaseController, FeedController):
 
-    def _check_security(self):        
+    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')        
+    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      
+            '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=c.project.rating)
+
+    """ 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()
+    def get_review_list(self, **kw):
+        self.review_list = Feedback.query.find({'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
+    """ 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)
+        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 
+        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 
+
+    # 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 
+        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
+    @require_post()
     @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
+    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
+    @require_post()
     @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
+        user_review = Feedback.query.find(
+            {'reported_by_id': c.user._id, 'project_id': c.project._id}
+            ).first()
+        if user_review:
+            Feedback.query.remove(dict(
+                {'reported_by_id': c.user._id, 'project_id': c.project._id}))
+            M.main_orm_session.flush()
+            self.getRating()
+            flash('Feedback successfully deleted')
+            return 'Success'
+        else:
+            flash('Feedback was not deleted')
+            return 'Failed'
+
+    # This method is used to check for profanity in feedback text
     @expose()
     def feedback_check(self, description=None):
         pf = ProfanityFilter()
@@ -195,36 +215,36 @@ class RootController(BaseController, FeedController):
             result = 'None'
         return result
 
-
-    # This method count the number of stars finds their sum and calculates the average of all the star rating count    
+    """ 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)
+        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
+            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):
+            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                  
+                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
old mode 100644
new mode 100755
index 19f031b..fc61e16
--- a/ForgeFeedback/forgefeedback/model/__init__.py
+++ b/ForgeFeedback/forgefeedback/model/__init__.py
@@ -16,6 +16,3 @@
 #       under the License.
 
 from feedback import Feedback
-
-
-
diff --git a/ForgeFeedback/forgefeedback/model/feedback.py b/ForgeFeedback/forgefeedback/model/feedback.py
old mode 100644
new mode 100755
index 2ef7326..7cb61e5
--- a/ForgeFeedback/forgefeedback/model/feedback.py
+++ b/ForgeFeedback/forgefeedback/model/feedback.py
@@ -1,4 +1,4 @@
-#	Licensed to the Apache Software Foundation (ASF) under one
+#       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
@@ -30,7 +30,7 @@ from ming.orm import FieldProperty, ForeignIdProperty, RelationProperty
 
 from allura.model.artifact import VersionedArtifact
 from allura.model.auth import AlluraUserProperty, User
-from allura.model.project import ProjectRole 
+from allura.model.project import ProjectRole
 from allura.model.timeline import ActivityObject
 from allura.lib import helpers as h
 
@@ -38,10 +38,10 @@ log = logging.getLogger(__name__)
 
 
 class Feedback(VersionedArtifact, ActivityObject):
-   
+
     class __mongometa__:
-	name = 'feedback'
-   	
+        name = 'feedback'
+
     type_s = 'Feedback'
     _id = FieldProperty(schema.ObjectId)
     created_date = FieldProperty(datetime, if_missing=datetime.utcnow)
@@ -57,7 +57,7 @@ class Feedback(VersionedArtifact, ActivityObject):
             created_date_dt=self.created_date,
             text=self.description,
         )
-        return result 
+        return result
 
     @property
     def activity_name(self):
@@ -66,17 +66,11 @@ class Feedback(VersionedArtifact, ActivityObject):
     @property
     def activity_extras(self):
         d = ActivityObject.activity_extras.fget(self)
-        d.update(summary=self.description) 
+        d.update(summary=self.description)
         return d
-    def url(self):
-      return self.app_config.url()
-
-Mapper.compile_all()
-
-
-
-
-
 
+    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
old mode 100644
new mode 100755
diff --git a/ForgeFeedback/forgefeedback/templates/__init__.py b/ForgeFeedback/forgefeedback/templates/__init__.py
old mode 100644
new mode 100755
diff --git a/ForgeFeedback/forgefeedback/templates/feedback/common_feedback.html b/ForgeFeedback/forgefeedback/templates/feedback/common_feedback.html
old mode 100644
new mode 100755
index 48db583..6bd6634
--- a/ForgeFeedback/forgefeedback/templates/feedback/common_feedback.html
+++ b/ForgeFeedback/forgefeedback/templates/feedback/common_feedback.html
@@ -15,7 +15,7 @@
        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 
+       This is used when user is trying to imput a new review 
 
 -#}
 
@@ -28,7 +28,7 @@ This is used when user is trying to imput a new review
 <!-- 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>
+		<p id="status_msg" style="color:#f33;display:none;"> <i class="fa fa-exclamation-triangle"></i> Profanity alert! Please update your feedback.</p>
 	</div>
 {% endmacro %}
 
@@ -59,27 +59,32 @@ function manage()
 
 </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>
+    $('#feedback_form').submit(function(event){
+    event.preventDefault();
+    var description = $("#description").val();
+    
+    $.ajax({
+        context: this,
+        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();
+                $('#button').attr('disabled', false);
+                this.submit();
+            }
+        }
+        
+    });
 });
 </script>
+
 {% endmacro %}
 
 
diff --git a/ForgeFeedback/forgefeedback/templates/feedback/edit_feedback.html b/ForgeFeedback/forgefeedback/templates/feedback/edit_feedback.html
old mode 100644
new mode 100755
index 8f11e2a..bf6ec75
--- a/ForgeFeedback/forgefeedback/templates/feedback/edit_feedback.html
+++ b/ForgeFeedback/forgefeedback/templates/feedback/edit_feedback.html
@@ -15,7 +15,7 @@
        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 
+       This is used when user is trying to imput a new review 
 
 -#}
 
@@ -39,7 +39,7 @@ p {
 </style>
 
 <div class="feed">
-<form action="{{ c.app.url }}edit_user_review" method="post">
+<form action="{{ c.app.url }}edit_user_review" method="post" id="feedback_form">
 
 <div class="row">
 <div class="col-25">
@@ -50,31 +50,12 @@ p {
 	</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 %} 
+    
+    <input type="radio" id="star5" name="rating" value="5" {% if rating == '5' %} checked="checked" {% endif %} onclick="manage()" /><label for="star5" title="Excellent"></label>
+    <input type="radio" id="star4" name="rating" value="4" {% if rating == '4' %} checked="checked" {% endif %} onclick="manage()" /><label for="star4" title="Great"></label>
+    <input type="radio" id="star3" name="rating" value="3" {% if rating == '3' %} checked="checked" {% endif %} onclick="manage()" /><label for="star3" title="Good"></label>
+    <input type="radio" id="star2" name="rating" value="2" {% if rating == '2' %} checked="checked" {% endif %} onclick="manage()" /><label for="star2" title="Average"></label>
+    <input type="radio" id="star1" name="rating" value="1" {% if rating == '1' %} checked="checked" {% endif %} onclick="manage()" /><label for="star1" title="Poor"></label>
     
 </fieldset>
 </div>
diff --git a/ForgeFeedback/forgefeedback/templates/feedback/feedback_list.html b/ForgeFeedback/forgefeedback/templates/feedback/feedback_list.html
old mode 100644
new mode 100755
diff --git a/ForgeFeedback/forgefeedback/templates/feedback/index.html b/ForgeFeedback/forgefeedback/templates/feedback/index.html
old mode 100644
new mode 100755
index fe96856..853c4d1
--- a/ForgeFeedback/forgefeedback/templates/feedback/index.html
+++ b/ForgeFeedback/forgefeedback/templates/feedback/index.html
@@ -9,7 +9,7 @@
 
          http://www.apache.org/licenses/LICENSE-2.0
 
- e     Unless required by applicable law or agreed to in writing,
+       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
@@ -38,7 +38,7 @@
 	{% 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>
+	<p><h2><a href="{{c.app.url}}edit_feedback"> Edit </a> or <a class="post-link" href="#"> Delete </a> your feedback for {{c.project.name}} </h2> </p>
 	{% endif %}  
 </div>
 {% else %}
@@ -50,7 +50,20 @@
         {% include 'forgefeedback:templates/feedback/feedback_list.html' %}
     </ul>  
 </div>
-{% endblock %}
 
+{% endblock %}
 
+{% block extra_js %}
+<script>
+    $('.post-link').click(function() {
+    var cval = $.cookie('_session_id');
+    $.post( '{{c.app.url}}delete_feedback', {'_session_id':cval},
+        function(){ 
+            window.location.href = '{{c.app.url}}';
+            }
+    );
+    
+    });
+</script>
+{% endblock %}
 
diff --git a/ForgeFeedback/forgefeedback/templates/feedback/new_feedback.html b/ForgeFeedback/forgefeedback/templates/feedback/new_feedback.html
old mode 100644
new mode 100755
index b472aab..bf93b3b
--- a/ForgeFeedback/forgefeedback/templates/feedback/new_feedback.html
+++ b/ForgeFeedback/forgefeedback/templates/feedback/new_feedback.html
@@ -15,7 +15,7 @@
        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 
+       This is used when user is trying to imput a new review 
 
 -#}
 {% import 'allura:templates/jinja_master/lib.html' as lib with context %}
@@ -37,7 +37,7 @@ p {
 </style>
 
 <div class="feed">
-    <form action="{{ c.app.url }}create_feedback" method="post">
+    <form action="{{ c.app.url }}create_feedback" method="post" id="feedback_form">
 
     <div class="row">
         <div class="col-25">
diff --git a/ForgeFeedback/forgefeedback/tests/__init__.py b/ForgeFeedback/forgefeedback/tests/__init__.py
old mode 100644
new mode 100755
index 77505f1..144e298
--- a/ForgeFeedback/forgefeedback/tests/__init__.py
+++ b/ForgeFeedback/forgefeedback/tests/__init__.py
@@ -14,4 +14,3 @@
 #       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
old mode 100644
new mode 100755
index 77505f1..144e298
--- a/ForgeFeedback/forgefeedback/tests/functional/__init__.py
+++ b/ForgeFeedback/forgefeedback/tests/functional/__init__.py
@@ -14,4 +14,3 @@
 #       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
old mode 100644
new mode 100755
index b97eafa..e99fdb5
--- a/ForgeFeedback/forgefeedback/tests/functional/test_root.py
+++ b/ForgeFeedback/forgefeedback/tests/functional/test_root.py
@@ -17,7 +17,8 @@
 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 nose.tools import assert_equal, assert_in, assert_not_in
+from nose.tools import assert_true, assert_false, assert_raises
 
 from allura import model as M
 from alluratest.controller import TestController
@@ -26,44 +27,46 @@ 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 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_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_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.post('/p/test/feedback/delete_feedback')
+        assert_in('Success', 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
+    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
old mode 100644
new mode 100755
index bd7f902..a8ea087
--- a/ForgeFeedback/forgefeedback/tests/test_feedback_roles.py
+++ b/ForgeFeedback/forgefeedback/tests/test_feedback_roles.py
@@ -25,12 +25,13 @@ 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_tool('u/test-user-1', 'feedback')
 @td.with_user_project('test-user-1')
 def setup_with_tools():
     setup_global_objects()
@@ -51,4 +52,3 @@ def test_role_assignments():
     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
old mode 100644
new mode 100755
index 9c6667a..193f766
--- a/ForgeFeedback/forgefeedback/tests/unit/__init__.py
+++ b/ForgeFeedback/forgefeedback/tests/unit/__init__.py
@@ -48,4 +48,3 @@ class FeedbackTestWithModel(object):
 
     def tearDown(self):
         ThreadLocalORMSession.close_all()
-
diff --git a/ForgeFeedback/forgefeedback/tests/unit/test_feedback.py b/ForgeFeedback/forgefeedback/tests/unit/test_feedback.py
old mode 100644
new mode 100755
index 604041e..03f6944
--- a/ForgeFeedback/forgefeedback/tests/unit/test_feedback.py
+++ b/ForgeFeedback/forgefeedback/tests/unit/test_feedback.py
@@ -22,20 +22,21 @@ 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')
+    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
old mode 100644
new mode 100755
index f99332c..4b000b1
--- a/ForgeFeedback/forgefeedback/tests/unit/test_root_controller.py
+++ b/ForgeFeedback/forgefeedback/tests/unit/test_root_controller.py
@@ -30,6 +30,7 @@ from forgefeedback.tests.unit import FeedbackTestWithModel
 from forgefeedback.model import Feedback
 from forgefeedback import feedback_main
 
+
 class TestFeedbackApp(FeedbackTestWithModel):
 
     def setUp(self):
@@ -39,28 +40,28 @@ class TestFeedbackApp(FeedbackTestWithModel):
 
     def test_index(self):
         reviews = feedback_main.RootController().index()
-        assert reviews['user_has_already_reviewed']==False
+        assert True if not reviews['user_has_already_reviewed'] else False
         create_feedbacks()
         reviews = feedback_main.RootController().index()
-        assert reviews['user_has_already_reviewed']==True
-        
+        assert True if reviews['user_has_already_reviewed'] else False
+
     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'
+        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)
+        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'
+        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')
@@ -68,13 +69,15 @@ class TestFeedbackApp(FeedbackTestWithModel):
         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')
+    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	
+    feedback = Feedback(rating=rating, description=description)
+    session(feedback).flush()
+    return feedback
diff --git a/ForgeFeedback/forgefeedback/version.py b/ForgeFeedback/forgefeedback/version.py
old mode 100644
new mode 100755
index 1333217..3ea72c5
--- a/ForgeFeedback/forgefeedback/version.py
+++ b/ForgeFeedback/forgefeedback/version.py
@@ -1,4 +1,4 @@
-#Licensed to the Apache Software Foundation (ASF) under one
+# 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
@@ -17,8 +17,3 @@
 
 __version_info__ = (0, 0)
 __version__ = '.'.join(map(str, __version_info__))
-
-
-
-
-
diff --git a/ForgeFeedback/setup.py b/ForgeFeedback/setup.py
old mode 100644
new mode 100755


[allura] 04/04: More polishing

Posted by br...@apache.org.
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 7f41e1514f224cad7394de389b08f1fa8d5e6898
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Mon Jan 6 17:06:29 2020 -0500

    More polishing
---
 Allura/allura/templates/jinja_master/nav_menu.html | 14 +++++++++----
 ForgeFeedback/forgefeedback/__init__.py            | 16 +++++++++++++++
 ForgeFeedback/forgefeedback/feedback_main.py       | 24 +++++++++++-----------
 ForgeFeedback/forgefeedback/model/feedback.py      |  4 ++++
 ForgeFeedback/forgefeedback/templates/__init__.py  | 16 +++++++++++++++
 5 files changed, 58 insertions(+), 16 deletions(-)

diff --git a/Allura/allura/templates/jinja_master/nav_menu.html b/Allura/allura/templates/jinja_master/nav_menu.html
index 9509334..3904896 100644
--- a/Allura/allura/templates/jinja_master/nav_menu.html
+++ b/Allura/allura/templates/jinja_master/nav_menu.html
@@ -64,6 +64,7 @@
     {% endif %}
 {% endif %}
 
+{% if c.project.rating %}{# hide if ratings haven't been used (project/site may not use ForgeFeedback) #}
 <script>
  document.getElementById("stars").innerHTML = getStars("{{c.project.rating}}");
 
@@ -74,17 +75,22 @@ function getStars(ratings) {
   let output = [];
 
   // Append all the filled whole stars
-  for (var i = ratings; i >= 1; i--)
+  var i;
+  for (i = ratings; i >= 1; i--) {
     output.push('<i class="fa fa-star" aria-hidden="true" style="color: orange;"></i>&nbsp;');
+  }
 
   // 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>&nbsp;');
+  if (i === .5) {
+    output.push('<i class="fa fa-star-half-o" aria-hidden="true" style="color: orange;"></i>&nbsp;');
+  }
 
   // Fill the empty stars
-  for (let i = (5 - ratings); i >= 1; i--)
+  for (let i = (5 - ratings); i >= 1; i--) {
     output.push('<i class="fa fa-star-o" aria-hidden="true" style="color: orange;"></i>&nbsp;');
-
+  }
   return output.join('');
 
  }
  </script>
+{% endif %}
\ No newline at end of file
diff --git a/ForgeFeedback/forgefeedback/__init__.py b/ForgeFeedback/forgefeedback/__init__.py
index e69de29..144e298 100755
--- a/ForgeFeedback/forgefeedback/__init__.py
+++ b/ForgeFeedback/forgefeedback/__init__.py
@@ -0,0 +1,16 @@
+#       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/feedback_main.py b/ForgeFeedback/forgefeedback/feedback_main.py
index 4f04b61..889c0f1 100755
--- a/ForgeFeedback/forgefeedback/feedback_main.py
+++ b/ForgeFeedback/forgefeedback/feedback_main.py
@@ -20,7 +20,7 @@ import pymongo
 
 # Non-stdlib imports
 
-from tg import expose, flash, url, config, request
+from tg import expose, flash, url, config, request, redirect
 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
@@ -121,7 +121,7 @@ class RootController(BaseController, FeedController):
         rating_by_user = Feedback.query.find({
             'reported_by_id': c.user._id, 'project_id': c.project._id}
         ).count()
-        if (rating_by_user > 0):
+        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,
@@ -150,8 +150,8 @@ class RootController(BaseController, FeedController):
         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())
+        self.getRating()  # force recalculation
+        redirect(c.app.url)
 
     # called on click of the Feedback link
     @with_trailing_slash
@@ -183,8 +183,8 @@ class RootController(BaseController, FeedController):
         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())
+        self.getRating()  # force recalculation
+        redirect(c.app.url)
 
     # called when user clicks on delete link in feedback page
     @without_trailing_slash
@@ -198,7 +198,7 @@ class RootController(BaseController, FeedController):
             Feedback.query.remove(dict(
                 {'reported_by_id': c.user._id, 'project_id': c.project._id}))
             M.main_orm_session.flush()
-            self.getRating()
+            self.getRating()  # force recalculation
             flash('Feedback successfully deleted')
             return 'Success'
         else:
@@ -231,7 +231,7 @@ class RootController(BaseController, FeedController):
         sum_of_ratings = float(
             fivestarcount + fourstarcount + threestarcount + twostarcount +
             onestarcount)
-        if (sum_of_ratings != 0):
+        if sum_of_ratings != 0:
             average_user_ratings = float(
                 (5*fivestarcount) + (4*fourstarcount) +
                 (3*threestarcount) + (2*twostarcount) +
@@ -239,12 +239,12 @@ class RootController(BaseController, FeedController):
             float_rating = float(average_user_ratings)
             int_rating = int(float_rating)
             float_point_value = float_rating - int_rating
-            if(float_point_value < 0.25):
+            if float_point_value < 0.25:
                 c.project.rating = int_rating
-            elif(float_point_value >= 0.25 < 0.75):
+            elif float_point_value >= 0.25 < 0.75:
                 c.project.rating = 0.5 + int_rating
-            elif(float_point_value >= 0.75):
+            elif float_point_value >= 0.75:
                 c.project.rating = float(int_rating)+1
             return average_user_ratings
-        if (sum_of_ratings == 0):
+        if sum_of_ratings == 0:
             c.project.rating = 0.0
diff --git a/ForgeFeedback/forgefeedback/model/feedback.py b/ForgeFeedback/forgefeedback/model/feedback.py
index 7cb61e5..9498f70 100755
--- a/ForgeFeedback/forgefeedback/model/feedback.py
+++ b/ForgeFeedback/forgefeedback/model/feedback.py
@@ -41,6 +41,9 @@ class Feedback(VersionedArtifact, ActivityObject):
 
     class __mongometa__:
         name = 'feedback'
+        indexes = [
+            ('project_id', 'reported_by_id'),
+        ]
 
     type_s = 'Feedback'
     _id = FieldProperty(schema.ObjectId)
@@ -55,6 +58,7 @@ class Feedback(VersionedArtifact, ActivityObject):
         result = VersionedArtifact.index(self)
         result.update(
             created_date_dt=self.created_date,
+            reported_by_username_t=self.reported_by.username,
             text=self.description,
         )
         return result
diff --git a/ForgeFeedback/forgefeedback/templates/__init__.py b/ForgeFeedback/forgefeedback/templates/__init__.py
index e69de29..144e298 100755
--- a/ForgeFeedback/forgefeedback/templates/__init__.py
+++ b/ForgeFeedback/forgefeedback/templates/__init__.py
@@ -0,0 +1,16 @@
+#       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.