Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User tag screening #735

Merged
merged 21 commits into from
Nov 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions frontend/lit/NestedTag.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import ReactDOM from "react-dom";
import {getReferenceTagListUrl} from "shared/utils/urls";

import PaginatedReferenceList from "./components/PaginatedReferenceList";

Expand Down Expand Up @@ -61,6 +62,10 @@ class NestedTag {
};
ReactDOM.render(<PaginatedReferenceList settings={settings} canEdit={canEdit} />, el);
}

get_list_link() {
return getReferenceTagListUrl(this.assessment_id, this.data.pk);
}
}

export default NestedTag;
1 change: 1 addition & 0 deletions frontend/lit/Reference.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Reference {
this.data = data;
this._quickSearchText = `${data.title}-${data.year}-${data.authors}-${data.authors_short}`.toLowerCase();
this.tags = data.tags.map(tagId => tagtree.dict[tagId]);
this.userTags = data.user_tags ? data.user_tags.map(tagId => tagtree.dict[tagId]) : [];
}

static get_detail_url(id, subtype) {
Expand Down
16 changes: 9 additions & 7 deletions frontend/lit/ReferenceTreeBrowse/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,15 +72,17 @@ class ReferenceTreeMain extends Component {
) : store.selectedTag === null ? (
<h4>Available references</h4>
) : (
<h4>
<span>
{selectedReferences && selectedReferences.length > 0
? `${filteredReferences.length} references tagged:`
: "References tagged:"}
</span>
<div className="d-flex align-items-center">
<h4>
<span>
{selectedReferences && selectedReferences.length > 0
? `${filteredReferences.length} references tagged:`
: "References tagged:"}
</span>
</h4>
<span className="ml-2 refTag">{store.selectedTag.get_full_name()}</span>
<span>{yearText}</span>
</h4>
</div>
)}
</div>
<div className="col-md-3">
Expand Down
46 changes: 38 additions & 8 deletions frontend/lit/TagReferences/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ class TagReferencesMain extends Component {
render() {
const {store} = this.props,
selectedReferencePk = store.selectedReference ? store.selectedReference.data.pk : null,
selectedReferenceTags = store.selectedReferenceTags ? store.selectedReferenceTags : [];
selectedReferenceTags = store.selectedReferenceTags ? store.selectedReferenceTags : [],
selectedReferenceUserTags = store.selectedReferenceUserTags
? store.selectedReferenceUserTags
: [];
return (
<div className="row">
<div className={store.filterClass} id="refFilter">
Expand Down Expand Up @@ -66,7 +69,11 @@ class TagReferencesMain extends Component {
{ref.tags.length > 0 ? (
<i
className="fa fa-tags"
title="tagged"
title={
store.config.conflict_resolution
? "has resolved tag(s)"
: "tagged"
}
aria-hidden="true"></i>
) : null}
</p>
Expand Down Expand Up @@ -118,9 +125,32 @@ class TagReferencesMain extends Component {
{selectedReferenceTags.map((tag, i) => (
<span
key={i}
title={tag.get_full_name()}
title={
store.config.conflict_resolution
? "Resolved Tag: ".concat(tag.get_full_name())
: tag.get_full_name()
}
className="refTag refTagEditing"
onClick={() => store.removeTag(tag)}>
onClick={
store.config.conflict_resolution
? null
: () => store.removeTag(tag)
}>
{this.state.showFullTag
? tag.get_full_name()
: tag.data.name}
</span>
))}
{selectedReferenceUserTags.map((tag, i) => (
<span
key={i}
title={tag.get_full_name()}
className="refTag refUserTag refTagEditing"
onClick={
store.config.conflict_resolution
? () => store.removeTag(tag)
: null
}>
{this.state.showFullTag
? tag.get_full_name()
: tag.data.name}
Expand All @@ -144,13 +174,13 @@ class TagReferencesMain extends Component {
extraActions={[
<div
className="dropdown-item cursor-pointer"
key={3}
key={4}
onClick={() => store.removeAllTags()}>
&nbsp;Remove all tags
</div>,
<div
className="dropdown-item cursor-pointer"
key={4}
key={5}
onClick={() => {
this.showFullTag.toggle();
this.setState({showFullTag: this.showFullTag.value});
Expand All @@ -163,7 +193,7 @@ class TagReferencesMain extends Component {
store.config.instructions.length > 0 ? (
<div
className="dropdown-item cursor-pointer"
key={5}
key={6}
onClick={() => store.setInstructionsModal(true)}>
&nbsp;View instructions
</div>
Expand Down Expand Up @@ -202,7 +232,7 @@ class TagReferencesMain extends Component {
<TagTree
tagtree={toJS(store.tagtree)}
handleTagClick={tag => store.addTag(tag)}
showTagHover={true}
showTagHoverAdd={true}
/>
</div>
<Modal
Expand Down
30 changes: 25 additions & 5 deletions frontend/lit/TagReferences/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class Store {
@observable references = [];
@observable selectedReference = null;
@observable selectedReferenceTags = null;
@observable selectedReferenceUserTags = null;
@observable errorOnSave = false;
@observable filterClass = "";
@observable showInstructionsModal = false;
Expand All @@ -29,22 +30,37 @@ class Store {
@action.bound changeSelectedReference(reference) {
this.selectedReference = reference;
this.selectedReferenceTags = reference.tags.slice(0); // shallow copy
this.selectedReferenceUserTags = reference.userTags.slice(0);
}
@action.bound addTag(tag) {
if (
this.selectedReference &&
!_.find(this.selectedReferenceTags, el => el.data.pk === tag.data.pk)
!_.find(
this.config.conflict_resolution
? this.selectedReferenceUserTags
: this.selectedReferenceTags,
el => el.data.pk === tag.data.pk
)
) {
this.selectedReferenceTags.push(tag);
this.config.conflict_resolution
? this.selectedReferenceUserTags.push(tag)
: this.selectedReferenceTags.push(tag);
}
}
@action.bound removeTag(tag) {
_.remove(this.selectedReferenceTags, el => el.data.pk === tag.data.pk);
_.remove(
this.config.conflict_resolution
? this.selectedReferenceUserTags
: this.selectedReferenceTags,
el => el.data.pk === tag.data.pk
);
}
@action.bound saveAndNext() {
const payload = {
pk: this.selectedReference.data.pk,
tags: this.selectedReferenceTags.map(tag => tag.data.pk),
tags: this.config.conflict_resolution
? this.selectedReferenceUserTags.map(tag => tag.data.pk)
: this.selectedReferenceTags.map(tag => tag.data.pk),
},
success = () => {
const $el = $(this.saveIndicatorElement),
Expand All @@ -61,9 +77,11 @@ class Store {
$el.fadeIn().fadeOut({
complete: () => {
this.selectedReference.tags = toJS(this.selectedReferenceTags);
this.selectedReference.userTags = toJS(this.selectedReferenceUserTags);
this.references.splice(index, 1, toJS(this.selectedReference));
this.selectedReference = null;
this.selectedReferenceTags = null;
this.selectedReferenceUserTags = null;
if (this.references.length > index + 1) {
this.changeSelectedReference(this.references[index + 1]);
} else {
Expand All @@ -82,7 +100,9 @@ class Store {
).fail(failure);
}
@action.bound removeAllTags() {
this.selectedReferenceTags = [];
this.config.conflict_resolution
? (this.selectedReferenceUserTags = [])
: (this.selectedReferenceTags = []);
}
@action.bound setSaveIndicatorElement(el) {
this.saveIndicatorElement = el;
Expand Down
6 changes: 4 additions & 2 deletions frontend/lit/components/Reference.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ class Reference extends Component {
authors = data.authors || data.authors_short || reference.NO_AUTHORS_TEXT,
year = data.year || "",
actionItems = [
<ActionLink key={1} label="Edit reference" href={data.editReferenceUrl} />,
<ActionLink key={0} label="Edit reference tags" href={data.editTagUrl} />,
<ActionLink key={1} label="Edit reference" href={data.editReferenceUrl} />,
<ActionLink key={2} label="Delete reference" href={data.deleteReferenceUrl} />,
<ActionLink key={3} label="Tag history" href={data.tagHistoryUrl} />,
].concat(extraActions);
Expand Down Expand Up @@ -155,6 +155,7 @@ class Reference extends Component {
{data.abstract ? (
<div
className="abstracts resize-y p-2"
style={data.abstract.length > 1500 ? {height: "45vh"} : null}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's nice - so it only sets a bound if it's a long abstract?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah that way longer abstracts aren't overwhelming on page load, but users can still expand to show the full abstract if they wish. and there's no weird oversized box with shorter abstracts

dangerouslySetInnerHTML={
keywordDict
? {__html: markKeywords(data.abstract, keywordDict)}
Expand All @@ -166,9 +167,10 @@ class Reference extends Component {
<p>
{tags.map((tag, i) => (
<a
style={{color: "white"}}
key={i}
href={getReferenceTagListUrl(data.assessment_id, tag.data.pk)}
className="referenceTag badge badge-info mr-1">
className="refTag mt-1">
{tag.get_full_name()}
</a>
))}
Expand Down
28 changes: 15 additions & 13 deletions frontend/lit/components/TagTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@ class TagNode extends Component {
};
}
render() {
const {tag, showReferenceCount, handleOnClick, selectedTag, showTagHover} = this.props,
tagClass =
tag === selectedTag
? "d-flex nestedTag selected align-items-center"
: "d-flex nestedTag align-items-center",
const {tag, showReferenceCount, handleOnClick, selectedTag, showTagHoverAdd} = this.props,
hasChildren = tag.children.length > 0,
expanderIcon = this.state.expanded ? "fa-minus" : "fa-plus",
toggleExpander = e => {
Expand All @@ -29,7 +25,9 @@ class TagNode extends Component {

return (
<>
<div className={tagClass} onClick={() => handleOnClick(tag)}>
<div
className={"d-flex nestedTag align-items-center"}
onClick={() => handleOnClick(tag)}>
<div
className="d-flex justify-content-end"
style={{width: (tag.depth - 1) * 10 + 25}}>
Expand All @@ -41,7 +39,11 @@ class TagNode extends Component {
</button>
) : null}
</div>
<div className={showTagHover ? "tagName" : null} style={{flex: 1}}>
<div
className={(showTagHoverAdd ? "tagHoverAdd" : "tagHoverSimple").concat(
tag === selectedTag ? " tagSelected" : ""
)}
style={{flex: 1}}>
<span>
{tag.data.name}
{showReferenceCount ? ` (${tag.get_references_deep().length})` : null}
Expand All @@ -56,7 +58,7 @@ class TagNode extends Component {
handleOnClick={handleOnClick}
showReferenceCount={showReferenceCount}
selectedTag={selectedTag}
showTagHover={showTagHover}
showTagHoverAdd={showTagHoverAdd}
/>
))
: null}
Expand All @@ -69,7 +71,7 @@ TagNode.propTypes = {
handleOnClick: PropTypes.func.isRequired,
showReferenceCount: PropTypes.bool.isRequired,
selectedTag: PropTypes.object,
showTagHover: PropTypes.bool,
showTagHoverAdd: PropTypes.bool,
};

@observer
Expand All @@ -82,7 +84,7 @@ class TagTree extends Component {
selectedTag,
untaggedHandleClick,
untaggedCount,
showTagHover,
showTagHoverAdd,
} = this.props;
return (
<div id="litTagtree" className="resize-y p-2 mt-2">
Expand All @@ -93,7 +95,7 @@ class TagTree extends Component {
handleOnClick={handleTagClick}
showReferenceCount={showReferenceCount}
selectedTag={selectedTag}
showTagHover={showTagHover}
showTagHoverAdd={showTagHoverAdd}
/>
))}
{untaggedHandleClick ? (
Expand All @@ -112,12 +114,12 @@ TagTree.propTypes = {
selectedTag: PropTypes.object,
untaggedCount: PropTypes.number,
untaggedHandleClick: PropTypes.func,
showTagHover: PropTypes.bool,
showTagHoverAdd: PropTypes.bool,
};
TagTree.defaultProps = {
showReferenceCount: false,
handleTagClick: h.noop,
showTagHover: false,
showTagHoverAdd: false,
};

export default TagTree;
13 changes: 13 additions & 0 deletions hawc/apps/lit/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import pandas as pd
from django.apps import apps
from django.contrib.postgres.aggregates import ArrayAgg
from django.core.exceptions import ValidationError
from django.core.validators import URLValidator
from django.db import models
Expand Down Expand Up @@ -351,6 +352,18 @@ def prune_tags(self, root_tag, pruned_tags, descendants: bool = False):
)
return self.exclude(query).distinct("pk")

def user_tags(self, user_id: int) -> dict[int, list[int]]:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed how we're building this dictionary for the serializer since it looked like the old way required a query for each reference if user references were found

# Return a dictionary of reference_id: list[tag_ids] items for all references in a queryset
# TODO - update to annotate queryset with Django 4.1?
# https://docs.djangoproject.com/en/4.1/ref/contrib/postgres/expressions/#arraysubquery-expressions
UserReferenceTag = apps.get_model("lit", "UserReferenceTag")
user_qs = (
UserReferenceTag.objects.filter(reference__in=self, user=user_id)
.annotate(tag_ids=ArrayAgg("tags__id"))
.values_list("reference_id", "tag_ids")
)
return {reference_id: tag_ids for reference_id, tag_ids in user_qs}


class ReferenceManager(BaseManager):

Expand Down
1 change: 1 addition & 0 deletions hawc/apps/lit/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,7 @@ def to_representation(self, instance):
)
ret["editReferenceUrl"] = reverse("lit:ref_edit", args=(instance.pk,))
ret["deleteReferenceUrl"] = reverse("lit:ref_delete", args=(instance.pk,))
ret["tagHistoryUrl"] = reverse("lit:tag-history", args=(instance.pk,))

ret["identifiers"] = [ident.to_dict() for ident in instance.identifiers.all()]
ret["searches"] = [search.to_dict() for search in instance.searches.all()]
Expand Down
2 changes: 1 addition & 1 deletion hawc/apps/lit/templates/lit/overview.html
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ <h3>Manually added references</h3>

window.app.startup("litStartup", function(lit){
let tagtree = new lit.TagTree(config.tags[0], config.assessment_id, null);
tagtree.render(document.getElementById('tags'))
tagtree.render(document.getElementById('tags'), {handleTagClick: (x) => {window.location.href = x.get_list_link()}})
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

});

const histo = document.getElementById("referenceYearHistogram");
Expand Down
Loading