Skip to content

Commit

Permalink
draft(tasks): specify task trigger
Browse files Browse the repository at this point in the history
Signed-off-by: David Wallace <[email protected]>
  • Loading branch information
MyPyDavid committed Jan 9, 2025
1 parent 5d3bc73 commit d19b19d
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 129 deletions.
212 changes: 140 additions & 72 deletions rdmo/conditions/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,63 @@ def is_locked(self):
return self.locked

def resolve(self, values, set_prefix=None, set_index=None):
"""
Resolves the condition and returns a detailed result.
Returns:
dict: {
'result': bool,
'reason': str,
'trigger_value': Any,
'trigger_question_url': str
}
"""
source_values = self._filter_values(values, set_prefix, set_index)

if not source_values and set_prefix:
set_prefix, set_index = self._get_higher_level_prefix(set_prefix)
return self.resolve(values, set_prefix, set_index)

resolver_map = {
self.RELATION_EQUAL: self._resolve_equal,
self.RELATION_NOT_EQUAL: self._resolve_not_equal,
self.RELATION_CONTAINS: self._resolve_contains,
self.RELATION_GREATER_THAN: self._resolve_greater_than,
self.RELATION_GREATER_THAN_EQUAL: self._resolve_greater_than_equal,
self.RELATION_LESSER_THAN: self._resolve_lesser_than,
self.RELATION_LESSER_THAN_EQUAL: self._resolve_lesser_than_equal,
self.RELATION_EMPTY: self._resolve_empty,
self.RELATION_NOT_EMPTY: self._resolve_not_empty,
}

resolver = resolver_map.get(self.relation)
if not resolver:
return {'result': False, 'reason': 'Unknown relation type', 'trigger_value': None,
'trigger_question_url': None}

result = resolver(source_values)

if result['result']:
question_url = self._get_question_url(source_values)
result['trigger_question_url'] = question_url

return result

def _get_question_url(self, values):
"""
Finds the question associated with the resolved value.
"""
for value in values:
# Find the first question matching the attribute in the catalog
question = next(
(q for q in value.project.catalog.questions if q.attribute == self.source),
None
)
if question:
return question.get_absolute_url(value) # Pass the value to `get_absolute_url`
return None

def _filter_values(self, values, set_prefix, set_index):
source_values = filter(lambda value: value.attribute == self.source, values)

if set_prefix is not None:
Expand All @@ -126,114 +183,125 @@ def resolve(self, values, set_prefix=None, set_index=None):
value.set_index == int(set_index) or value.set_collection is False
), source_values)

source_values = list(source_values)
if not source_values:
if set_prefix:
# try one level higher
rpartition = set_prefix.rpartition('|')
set_prefix, set_index = rpartition[0], int(rpartition[2])
return self.resolve(values, set_prefix, set_index)

if self.relation == self.RELATION_EQUAL:
return self._resolve_equal(source_values)

elif self.relation == self.RELATION_NOT_EQUAL:
return not self._resolve_equal(source_values)

elif self.relation == self.RELATION_CONTAINS:
return self._resolve_contains(source_values)

elif self.relation == self.RELATION_GREATER_THAN:
return self._resolve_greater_than(source_values)

elif self.relation == self.RELATION_GREATER_THAN_EQUAL:
return self._resolve_greater_than_equal(source_values)

elif self.relation == self.RELATION_LESSER_THAN:
return self._resolve_lesser_than(source_values)
return list(source_values)

elif self.relation == self.RELATION_LESSER_THAN_EQUAL:
return self._resolve_lesser_than_equal(source_values)

elif self.relation == self.RELATION_EMPTY:
return not self._resolve_not_empty(source_values)

elif self.relation == self.RELATION_NOT_EMPTY:
return self._resolve_not_empty(source_values)

else:
return False
def _get_higher_level_prefix(self, set_prefix):
rpartition = set_prefix.rpartition('|')
return rpartition[0], int(rpartition[2])

def _resolve_equal(self, values):
results = []

for value in values:
if self.target_option:
results.append(value.option == self.target_option)
else:
results.append(value.text == self.target_text)

return True in results
if self.target_option and value.option == self.target_option:
return {
'result': True,
'reason': f"Value matches target option {self.target_option}.",
'trigger_value': value.option
}
elif not self.target_option and value.text == self.target_text:
return {
'result': True,
'reason': f"Value matches target text '{self.target_text}'.",
'trigger_value': value.text
}
return {'result': False, 'reason': 'No matching value found.', 'trigger_value': None}

def _resolve_not_equal(self, values):
for value in values:
if self.target_option and value.option != self.target_option:
return {
'result': True,
'reason': f"Value does not match target option {self.target_option}.",
'trigger_value': value.option
}
elif not self.target_option and value.text != self.target_text:
return {
'result': True,
'reason': f"Value does not match target text '{self.target_text}'.",
'trigger_value': value.text
}
return {'result': False, 'reason': 'All values match the condition.', 'trigger_value': None}

def _resolve_contains(self, values):
results = []

for value in values:
results.append(self.target_text in value.text)

return True in results
if self.target_text in value.text:
return {
'result': True,
'reason': f"Value contains target text '{self.target_text}'.",
'trigger_value': value.text
}
return {'result': False, 'reason': 'No value contains the target text.', 'trigger_value': None}

def _resolve_greater_than(self, values):

for value in values:
try:
if float(value.text) > float(self.target_text):
return True
return {
'result': True,
'reason': f"Value {value.text} is greater than target {self.target_text}.",
'trigger_value': value.text
}
except ValueError:
pass

return False
continue
return {'result': False, 'reason': 'No value is greater than the target.', 'trigger_value': None}

def _resolve_greater_than_equal(self, values):

for value in values:
try:
if float(value.text) >= float(self.target_text):
return True
return {
'result': True,
'reason': f"Value {value.text} is greater than or equal to target {self.target_text}.",
'trigger_value': value.text
}
except ValueError:
pass

return False
continue
return {'result': False, 'reason': 'No value is greater than or equal to the target.', 'trigger_value': None}

def _resolve_lesser_than(self, values):

for value in values:
try:
if float(value.text) < float(self.target_text):
return True
return {
'result': True,
'reason': f"Value {value.text} is less than target {self.target_text}.",
'trigger_value': value.text
}
except ValueError:
pass

return False
continue
return {'result': False, 'reason': 'No value is less than the target.', 'trigger_value': None}

def _resolve_lesser_than_equal(self, values):

for value in values:
try:
if float(value.text) <= float(self.target_text):
return True
return {
'result': True,
'reason': f"Value {value.text} is less than or equal to target {self.target_text}.",
'trigger_value': value.text
}
except ValueError:
pass
continue
return {'result': False, 'reason': 'No value is less than or equal to the target.', 'trigger_value': None}

return False
def _resolve_empty(self, values):
for value in values:
if not value.text and not value.option:
return {
'result': True,
'reason': "Value is empty.",
'trigger_value': None
}
return {'result': False, 'reason': 'All values are non-empty.', 'trigger_value': None}

def _resolve_not_empty(self, values):

for value in values:
if bool(value.text) or bool(value.option):
return True

return False
return {
'result': True,
'reason': "Value is not empty.",
'trigger_value': value.text or value.option
}
return {'result': False, 'reason': 'All values are empty.', 'trigger_value': None}

@classmethod
def build_uri(cls, uri_prefix, uri_path):
Expand Down
3 changes: 1 addition & 2 deletions rdmo/projects/models/issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ def get_absolute_url(self):

def resolve(self, values):
for condition in self.task.conditions.all():
if condition.resolve(values):
return True
return condition.resolve(values)

@property
def dates(self):
Expand Down
11 changes: 9 additions & 2 deletions rdmo/projects/templates/projects/issue_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,20 @@ <h3>{% trans 'Questions' %}</h3>
<p><strong>{{ question.text }}</strong></p>
{% endfor %}

{% for value in source.values %}
<p>{{ value.value_and_unit }}</p>
{% for resolved_value in source.resolved_values %}
<p>
Value: {{ resolved_value.value }}<br>
Reason: {{ resolved_value.reason }}<br>
{% if resolved_value.trigger_question_url %}
Triggered by: <a href="{{ resolved_value.trigger_question_url }}">View Question</a>
{% endif %}
</p>
{% endfor %}
</li>
{% endfor %}
</ul>


{% if issue.resources.all %}

<h3>{% trans 'External resources for this task' %}</h3>
Expand Down
83 changes: 49 additions & 34 deletions rdmo/projects/templates/projects/project_detail_issues.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,41 +28,56 @@ <h2>{% trans 'Tasks' %}</h2>
{% endif %}
</th>
</thead>
<tbody>
{% for issue in issues %}
<tr>
<td>
<a href="{% url 'issue' project.pk issue.pk %}">
{{ issue.task.title|markdown }}
</a>
</td>
<td>{{ issue.task.text|markdown }}</td>
<td>
{% for dates in issue.dates %}
{% if dates|length > 1 %}
<p>{{ dates.0 | date:"DATE_FORMAT" }}<br /> - {{ dates.1 | date:"DATE_FORMAT" }}</p>
{% else %}
<p>{{ dates.0 | date:"DATE_FORMAT" }}</p>
{% endif %}
{% endfor %}
</td>
<td>{{ issue.get_status_display }}</td>
<td class="text-right">
<a href="{% url 'issue' project.pk issue.pk %}" class="fa fa-eye"
title="{% trans 'Show task' %}">
</a>
<a href="{% url 'issue_update' project.pk issue.pk %}" class="fa fa-pencil"
title="{% trans 'Update task status' %}">
</a>
{% if settings.PROJECT_SEND_ISSUE %}
<a href="{% url 'issue_send' project.pk issue.pk %}" class="fa fa-paper-plane"
title="{% trans 'Send task' %}">
</a>
{% endif %}
</td>
</tr>
<tbody>
{% for resolved_issue in issues %}
<tr>
<td>
<a href="{% url 'issue' project.pk resolved_issue.issue.pk %}">
{{ resolved_issue.issue.task.title|markdown }}
</a>
</td>
<td>
{{ resolved_issue.issue.task.text|markdown }}
<ul>
{% for condition in resolved_issue.conditions %}
<li>
<strong>Condition:</strong> {{ condition.condition.relation_label }}<br>
<strong>Reason:</strong> {{ condition.reason }}<br>
{% if condition.trigger_question_url %}
<strong>Triggered by:</strong>
<a href="{{ condition.trigger_question_url }}">View Question</a>
{% endif %}
</li>
{% endfor %}
</tbody>
</ul>
</td>
<td>
{% for dates in resolved_issue.issue.dates %}
{% if dates|length > 1 %}
<p>{{ dates.0 | date:"DATE_FORMAT" }}<br /> - {{ dates.1 | date:"DATE_FORMAT" }}</p>
{% else %}
<p>{{ dates.0 | date:"DATE_FORMAT" }}</p>
{% endif %}
{% endfor %}
</td>
<td>{{ resolved_issue.issue.get_status_display }}</td>
<td class="text-right">
<a href="{% url 'issue' project.pk resolved_issue.issue.pk %}" class="fa fa-eye"
title="{% trans 'Show task' %}">
</a>
<a href="{% url 'issue_update' project.pk resolved_issue.issue.pk %}" class="fa fa-pencil"
title="{% trans 'Update task status' %}">
</a>
{% if settings.PROJECT_SEND_ISSUE %}
<a href="{% url 'issue_send' project.pk resolved_issue.issue.pk %}" class="fa fa-paper-plane"
title="{% trans 'Send task' %}">
</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>

</table>

{% else %}
Expand Down
Loading

0 comments on commit d19b19d

Please sign in to comment.