Skip to content

Commit

Permalink
Merge pull request #124 from edemocracy/testing_fixes
Browse files Browse the repository at this point in the history
Various fixes and improvements from testing feedback
  • Loading branch information
dpausp authored Feb 14, 2024
2 parents a688a2e + 0181f64 commit cdd651a
Show file tree
Hide file tree
Showing 20 changed files with 312 additions and 149 deletions.
2 changes: 2 additions & 0 deletions src/ekklesia_portal/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def app_setting_section():
"log_environment_on_startup": False,
"internal_login_enabled": True,
"languages": ["de", "en"],
"timezone": "UTC",
"login_visible": False,
"report_url": None,
"source_code_url": "https://github.com/edemocracy/ekklesia-portal",
Expand Down Expand Up @@ -276,5 +277,6 @@ def make_wsgi_app(settings_filepath=None, testing=False):
database.configure_sqlalchemy(app.settings.database, testing)
app.babel_init()
app.babel.localeselector(get_locale)
app.babel.timezoneselector(lambda: app.settings.app.timezone)

return app
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ body
input.form-control(type="text", placeholder=_("search_for"), name="search", value=search_query)
button.search_button(type="submit")
i.fas.fa-search  
= _('button_search')
= _('button_search')

if current_user is not none
|  
Expand All @@ -54,12 +54,12 @@ body
form.top_logout(action=logout_action, method="POST")
button.logout_button(type="submit")
i.fas.fa-sign-out-alt  
= _('button_logout')
= _('button_logout')
else
if show_login_button
a.login_button(href=login_url)
i.fas.fa-sign-in-alt  
= _("button_login")
= _("button_login")


- block messages
Expand Down
13 changes: 7 additions & 6 deletions src/ekklesia_portal/concepts/proposition/proposition_cells.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ def history(self):
PropositionStatus.CHANGING: 'submitted',
PropositionStatus.SUBMITTED: 'submitted',
PropositionStatus.ABANDONED: 'submitted',
PropositionStatus.QUALIFIED: 'submitted',
PropositionStatus.QUALIFIED: 'qualified',
PropositionStatus.SCHEDULED: 'scheduled',
PropositionStatus.VOTING: 'scheduled',
PropositionStatus.VOTING: 'voting',
PropositionStatus.FINISHED: 'finished',
}
variant = status_to_variant[self._model.status]
Expand Down Expand Up @@ -459,7 +459,7 @@ def _prepare_form_for_render(self):
departments = self._request.current_user.departments

tags = self._request.q(Tag).all()
items = items_for_proposition_select_widgets(departments, tags, selected_tag_names)
items = items_for_proposition_select_widgets(departments, tags, selected_tags=selected_tag_names)
self._form.prepare_for_render(items)

def department_name(self):
Expand Down Expand Up @@ -507,17 +507,18 @@ class PropositionsCell(LayoutCell):
'tag_values',
'type',
'without_tag_values',
'only_supporting'
]

pager = Cell.fragment("propositions_pager")

def propositions(self):
is_admin = self.current_user and self._request.identity.has_global_admin_permissions
return list(self._model.propositions(self._request.q, is_admin))
return list(self._model.propositions(self._request.q, self.current_user, is_admin))

def prop_count(self):
is_admin = self.current_user and self._request.identity.has_global_admin_permissions
return self._model.propositions(self._request.q, is_admin, count=True)
return self._model.propositions(self._request.q, self.current_user, is_admin, count=True)

def page_count(self):
per_page = self.prop_per_page
Expand Down Expand Up @@ -607,7 +608,7 @@ def _prepare_form_for_render(self):
selected_tag_names = self._form.cstruct['tags']

tags = self._request.q(Tag).all()
items = items_for_proposition_select_widgets([], tags, selected_tag_names)
items = items_for_proposition_select_widgets([], tags, selected_tags=selected_tag_names)
self._form.prepare_for_render(items)

def department_name(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
'related_proposition_id': HiddenWidget()
}


class PropositionSchema(Schema):
title = string_property(title=_('title'), validator=Length(min=5, max=255))
title = string_property(title=_('title'), validator=Length(min=7, max=255))
content = string_property(title=_('content'), validator=Length(min=10, max=100_000))
motivation = string_property(title=_('motivation'), missing='', validator=Length(max=100_000))
tags = set_property(title=_('tags'), missing=tuple())
Expand Down Expand Up @@ -48,7 +49,7 @@ class PropositionEditSchema(PropositionSchema):


class PropositionNewDraftSchema(Schema):
title = string_property(title=_('title'), validator=Length(min=5, max=255))
title = string_property(title=_('title'), validator=Length(min=7, max=255))
content = string_property(title=_('content'), validator=Length(min=10, max=100_000))
motivation = string_property(title=_('motivation'), missing='', validator=Length(max=100_000))
tags = set_property(title=_('tags'), missing=tuple())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def index_csv(self, request):
optional_fields = TableRowOptionalFields()
optional_fields.submitters = is_global_admin
content = propositions_to_csv(
self.propositions(request.q, is_global_admin),
self.propositions(request.q, request.current_user, is_global_admin),
origin=request.app.settings.common.instance_name,
optional_fields=optional_fields
)
Expand Down
21 changes: 16 additions & 5 deletions src/ekklesia_portal/concepts/proposition/propositions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
from sqlalchemy.orm import joinedload
from sqlalchemy.sql.functions import coalesce

from ekklesia_common.lid import LID
from ekklesia_portal.datamodel import Ballot, Changeset, Department, Proposition, PropositionType, SubjectArea, Tag, VotingPhase
from ekklesia_portal.enums import PropositionStatus, PropositionVisibility, PropositionRelationType
from ekklesia_portal.datamodel import Ballot, Changeset, Department, Proposition, PropositionType, SubjectArea, Tag, VotingPhase, Supporter
from ekklesia_portal.enums import PropositionStatus, PropositionVisibility, PropositionRelationType, SupporterStatus


@dataclass
Expand All @@ -31,6 +30,7 @@ class Propositions:
association_type: PropositionRelationType = None
association_id: str = None
include_amendments: str = None
only_supporting: str = None
# Initialization with numbers instead of None is necessary because otherwise the
# query values are not actually converted to an integer on assignment
page: Optional[int] = 1 # Ranges: x<=1 = None => First page; x>1 => Show page x
Expand All @@ -51,6 +51,7 @@ def __post_init__(self):
self.type = self.type or None
self.status = self.status or None
self.include_amendments = self.include_amendments or None
self.only_supporting = self.only_supporting or None

self.status_values = None
self.tag_values = None
Expand Down Expand Up @@ -163,6 +164,8 @@ def parse_search_filter(self, key, value):
self.visibility = value
case "include_amendments":
self.include_amendments = value
case "only_supporting":
self.only_supporting = value

def build_search_query(self):
query = []
Expand Down Expand Up @@ -190,6 +193,8 @@ def build_search_query(self):
query.append("visibility:" + self.maybe_add_quotes(self.visibility))
if self.include_amendments:
query.append("include_amendments:" + self.include_amendments)
if self.only_supporting:
query.append("only_supporting:" + self.only_supporting)

return " ".join(query)

Expand All @@ -204,7 +209,7 @@ def maybe_add_quotes(value):
return value

@log_call
def propositions(self, q, is_admin=False, count=False):
def propositions(self, q, current_user, is_admin=False, count=False):

Message.log(
message_type="propositions_filters",
Expand Down Expand Up @@ -275,7 +280,13 @@ def propositions(self, q, is_admin=False, count=False):
propositions = propositions.filter(~Proposition.tags.contains(tag))

if not self.include_amendments:
propositions = propositions.filter(Proposition.modifies_id.is_(None))
propositions = propositions.filter(Proposition.modifies_id.is_(None))

if self.only_supporting:
propositions = (
propositions.join(Supporter).filter(Supporter.member_id == current_user.id
).filter(Supporter.status == SupporterStatus.ACTIVE)
)

if count:
propositions = propositions.count()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ if show_full_history
= _('submitted_on', date=submitted_at|dateformat)

.proposition_history_item
if voting_phase.target
= _('finished_on', date=voting_phase.target|dateformat)
if voting_phase.voting_end
= _('finished_on', date=voting_phase.voting_end|dateformat)
else
= _('finished_no_date')
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
if show_full_history
if author
.proposition_history_item
= _("created_by", author=author.name)

if created_at
.proposition_history_item
= _("created_on", date=created_at|dateformat)

if submitted_at
.proposition_history_item
= _('submitted_on', date=submitted_at|dateformat)

.proposition_history_item
if qualified_at
= _('qualified_on', date=qualified_at|dateformat)
else
= _('qualified_no_date')
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,14 @@ if show_full_history
.proposition_history_item
= _('qualified_on', date=qualified_at|dateformat)

.proposition_history_item
if voting_phase.target
= _('voting_ends_at', datetime=voting_phase.target|datetimeformat)
else
if voting_phase.voting_start or voting_phase.voting_end
if voting_phase.voting_start:
.proposition_history_item
= _('voting_starts_at', datetime=voting_phase.voting_start|datetimeformat)

if voting_phase.voting_end:
.proposition_history_item
= _('voting_ends_at', datetime=voting_phase.voting_end|datetimeformat)
else
.proposition_history_item
= _('voting_no_date')
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
if show_full_history
if author
.proposition_history_item
= _("created_by", author=author.name)

if created_at
.proposition_history_item
= _("created_on", date=created_at|dateformat)

if submitted_at
.proposition_history_item
= _('submitted_on', date=submitted_at|dateformat)

if qualified_at
.proposition_history_item
= _('qualified_on', date=qualified_at|dateformat)

.proposition_history_item
if voting_phase.voting_end
= _('voting_ends_at', datetime=voting_phase.voting_end|datetimeformat)
else
= _('voting_no_date')
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@
li
a.btn_filter_remove(href=change_self_link(without_tags=none), title=_('remove_filter')) X

if only_supporting
li.nav-item
ul.proposition_filter
li
= _('only_supporting')
li
a.btn_filter_remove(href=change_self_link(only_supporting = none), title=_('remove_filter')) X

div.page-selector
= pager()

Expand Down
3 changes: 3 additions & 0 deletions src/ekklesia_portal/concepts/user/templates/user.j2.jade
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
&nbsp;
if member_in_area
= _('member_in_area')
a(href=supported_link(subject_area=area))
= _('supported_propositions')
br
if area in supported_areas
= _('cant_leave_supporting_area')
else
Expand Down
18 changes: 15 additions & 3 deletions src/ekklesia_portal/concepts/user/user_cells.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
from ekklesia_portal.app import App
from ekklesia_portal.concepts.ekklesia_portal.cell.form import EditFormCell
from ekklesia_portal.concepts.ekklesia_portal.cell.layout import LayoutCell
from ekklesia_portal.concepts.proposition import Propositions
from ekklesia_portal.concepts.user.user_helper import items_for_user_select_widgets
from ekklesia_portal.datamodel import Group, User, UserProfile
from ekklesia_portal.datamodel import Group, User, UserProfile, SubjectArea
from ekklesia_portal.enums import PropositionStatus, SupporterStatus


Expand Down Expand Up @@ -40,8 +41,19 @@ def member_area_action(self):
return self.link(self._model, name='member_area')

def supported_areas(self):
return [support.proposition.ballot.area for support in self._model.member_propositions \
if support.status == SupporterStatus.ACTIVE and support.proposition.status == PropositionStatus.SUBMITTED]
return [
support.proposition.ballot.area for support in self._model.member_propositions
if support.status == SupporterStatus.ACTIVE and support.proposition.status == PropositionStatus.SUBMITTED
]

def supported_link(self, subject_area: SubjectArea):
return self.class_link(
Propositions, {
"department": subject_area.department.name,
"subject_area": subject_area.name,
"only_supporting": "yes"
}
)


@App.cell('edit')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from colander import Length
from deform import Button
from deform.widget import SelectWidget, TextAreaWidget
from ekklesia_common.contract import Form, Schema, bool_property, date_property, enum_property, int_property, json_property, string_property
from ekklesia_common.contract import Form, Schema, bool_property, datetime_property, enum_property, int_property, json_property, string_property
from ekklesia_common.translation import _

from ekklesia_portal.enums import VotingStatus
Expand All @@ -10,7 +10,7 @@
class VotingPhaseSchema(Schema):
name = string_property(title=_('name'), validator=Length(max=23), missing='')
title = string_property(title=_('title'), validator=Length(max=160), missing='')
target = date_property(title=_('target'), description=_('voting_phase_target_description'), missing=None)
target = datetime_property(title=_('target'), description=_('voting_phase_target_description'), missing=None)
status = enum_property(VotingStatus, title=_('voting_status'))
department_id = int_property(title=_('department'))
phase_type_id = int_property(title=_('voting_phase_type'))
Expand Down
7 changes: 5 additions & 2 deletions src/ekklesia_portal/datamodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ def registration_end(self):

days = self.registration_end_days or self.phase_type.registration_end_days or 0

return self.target - timedelta(days=days)
return self.target - timedelta(days=days) - timedelta(seconds=1)

@property
def voting_can_be_created(self):
Expand All @@ -426,7 +426,10 @@ def voting_start(self):

@property
def voting_end(self):
return self.target
if self.target is None:
return

return self.target - timedelta(seconds=1)


class Supporter(Base): # §3.5
Expand Down
15 changes: 11 additions & 4 deletions src/ekklesia_portal/lib/discourse.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,26 @@ class DiscourseError(Exception):
pass


def create_discourse_topic(config: DiscourseConfig, topic: DiscourseTopic):
def create_discourse_topic(config: DiscourseConfig, topic: DiscourseTopic, with_tags: bool = True):

headers = {'accept': 'application/json', 'api-key': config.api_key, 'api-username': config.api_username}

req = {'raw': topic.content, 'category': config.category, 'title': topic.title, 'tags': topic.tags}
if with_tags:
req = {'raw': topic.content, 'category': config.category, 'title': topic.title, 'tags': topic.tags}
else:
req = {'raw': topic.content, 'category': config.category, 'title': topic.title}

with start_action(action_type="discourse_post") as action:
with start_action(action_type="discourse_post", with_tags=with_tags) as action:
resp = requests.post(f"{config.base_url}/posts.json", json=req, headers=headers)
action.add_success_fields(response=resp.json())

try:
resp.raise_for_status()
except HTTPError as e:
raise DiscourseError(e)
if e.response.status_code == 422 and with_tags:
# Try again once without tags
return create_discourse_topic(config, topic, with_tags=False)
else:
raise DiscourseError(e)

return resp
Loading

0 comments on commit cdd651a

Please sign in to comment.