Skip to content

Commit

Permalink
Add support for custom review tags (#1527)
Browse files Browse the repository at this point in the history
Co-authored-by: Vladislav Gerasimov <vladislav.gerasimov@movingimage.com>
Co-authored-by: Lisa Gaudette <lisa.gaudette@gmail.com>
Co-authored-by: Sherin Thomas <sherin.thomas.m@gmail.com>
Co-authored-by: Jonathan de Bruin <jonathandebruinos@gmail.com>
  • Loading branch information
5 people committed Jan 6, 2024
1 parent 3a615da commit 6ef67ab
Show file tree
Hide file tree
Showing 27 changed files with 1,160 additions and 60 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,6 @@ sketch
notes.md

flask_config.*

*.iml
.idea
9 changes: 7 additions & 2 deletions asreview/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,17 @@
"""Git implementation of _version.py."""

import errno
import functools
import os
import re
import subprocess
import sys
from typing import Any, Callable, Dict, List, Optional, Tuple
import functools
from typing import Any
from typing import Callable
from typing import Dict
from typing import List
from typing import Optional
from typing import Tuple


def get_keywords() -> Dict[str, str]:
Expand Down
132 changes: 132 additions & 0 deletions asreview/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@
"notes": ["notes"],
"keywords": ["keywords"],
"doi": ["doi"],
"custom_metadata_json": [
"custom metadata",
"custom metadata json",
"custom_metadata_json",
],
}

# the schema describes the content of the ASReview project file.
Expand Down Expand Up @@ -292,6 +297,133 @@
],
},
},
"tags": {
"$id": "#/properties/tags",
"type": ["array", "null"],
"title": "The tags schema",
"description": "The list of tags to show during review.",
"default": [],
"additionalItems": False,
"items": {
"$id": "#/properties/tags/items",
"anyOf": [
{
"$id": "#/properties/tags/items/anyOf/0",
"type": "object",
"title": "The first anyOf schema",
"description": "An explanation about the purpose of this instance.",
"required": ["name", "id", "values"],
"properties": {
"id": {
"$id": "#/properties/tags/items/anyOf/0/properties/id",
"type": "string",
"title": "The id of category.",
"description": "A unique identifier of the category.",
"default": "",
"examples": ["biomes"],
},
"name": {
"$id": "#/properties/tags/items/anyOf/0/properties/name",
"type": "string",
"title": "The name of category.",
"description": "A display name of the category.",
"default": "",
"examples": ["Biomes"],
},
"values": {
"$id": "#/properties/tags/items/anyOf/0/properties/values",
"type": "array",
"title": "The tag list of the category.",
"description": "The tag list of the category.",
"default": [],
"items": {
"$id": "#/properties/tags/items/anyOf/0/properties/values/items",
"anyOf": [
{
"$id": "#/properties/tags/items/anyOf/0/properties/values/items/anyOf/0",
"type": "object",
"required": ["id", "name"],
"properties": {
"id": {
"$id": "#/properties/tags/items/anyOf/0/properties/values/items"
"/anyOf/0/properties/id",
"type": "string",
"title": "The id of tag.",
"description": "A unique identifier of the tag.",
"default": "",
"examples": ["boreal_forest"],
},
"name": {
"$id": "#/properties/tags/items/anyOf/0/properties/values/items"
"/anyOf/0/properties/name",
"type": "string",
"title": "The name of tag.",
"description": "A display name of the tag.",
"default": "",
"examples": ["Boreal Forest"],
},
},
}
],
},
},
},
"additionalProperties": False,
"examples": [
{
"name": "Biomes",
"id": "biomes",
"values": [
{"id": "boreal_forest", "name": "Boreal Forest"},
{"id": "savanna", "name": "Savanna"},
{"id": "mangrove", "name": "Mangrove"},
{
"id": "tropical_forest",
"name": "Tropical Forest",
},
],
}
],
}
],
},
"examples": [
[
{
"name": "Biomes",
"id": "biomes",
"values": [
{"id": "boreal_forest", "name": "Boreal Forest"},
{"id": "savanna", "name": "Savanna"},
{"id": "mangrove", "name": "Mangrove"},
{"id": "tropical_forest", "name": "Tropical Forest"},
],
},
{
"name": "Restoration Approaches",
"id": "restoration_approaches",
"values": [
{
"id": "direct_seeding",
"name": "Direct seeding (i.e. spreading/planting seeds)",
},
{
"id": "tree_planting",
"name": "Planting trees (i.e. planting trees as seedlings)",
},
{
"id": "assisted_natural_regeneration",
"name": "Assisted natural regeneration",
},
{
"id": "farmer_managed_natural_regeneration",
"name": "Farmer managed natural regeneration",
},
],
},
]
],
},
"dataset_path": {
"$id": "#/properties/dataset_path",
"type": ["string", "null"],
Expand Down
2 changes: 2 additions & 0 deletions asreview/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ def create(
project_name=None,
project_description=None,
project_authors=None,
project_tags=None,
):
"""Initialize the necessary files specific to the web app."""

Expand Down Expand Up @@ -253,6 +254,7 @@ def create(
"datetimeCreated": str(datetime.now()),
"reviews": [],
"feature_matrices": [],
"tags": project_tags,
}

# validate new config before storing
Expand Down
25 changes: 25 additions & 0 deletions asreview/state/compatibility.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import sqlite3


def _alter_tag_column(state):
"""Add a tag column to the results table.
Introduced in state version 1.1.
"""
con: sqlite3.Connection = state.connect_to_sql_wr()
con.execute("ALTER TABLE results ADD COLUMN custom_metadata_json TEXT")


def check_and_update_version(current_version, new_version, state):

if current_version == new_version:
return current_version

if current_version in ["1.0", "1", 1] and new_version == "1.1":
_alter_tag_column(state)
return "1.1"

raise ValueError(
f"Migration script from version {current_version} "
f"to {new_version} doesn't exist"
)
35 changes: 35 additions & 0 deletions asreview/state/custom_metadata_mapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright 2019-2023 The ASReview Authors. All Rights Reserved.
#
# Licensed 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 json


def convert_to_custom_metadata_str(tags=None):
return json.dumps({"tags": tags})


def extract_tags(custom_metadata_str):
if not isinstance(custom_metadata_str, str):
return None

obj = json.loads(custom_metadata_str)

if "tags" in obj:
return obj["tags"]
else:
return None


def get_tag_composite_id(group_id, tag_id):
return f"{group_id}:{tag_id}"

0 comments on commit 6ef67ab

Please sign in to comment.