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

Add initial models, UI components for controlled list manager #10541 #10544

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import ko from 'knockout';
import { createApp } from 'vue';
import PrimeVue from 'primevue/config';
import ControlledListManagerApp from '@/App.vue';
import ControlledListManager from 'templates/views/components/plugins/controlled-list-manager.htm';

import 'primevue/resources/themes/bootstrap4-light-blue/theme.css';

ko.components.register('controlled-list-manager', {
viewModel: function() {
const app = createApp(ControlledListManagerApp);
app.use(PrimeVue);
app.mount('#controlled-list-mounting-point');
},
template: ControlledListManager,
});
3 changes: 3 additions & 0 deletions arches/app/models/fields/i18n.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from django.utils.translation import gettext_lazy as _
from django.db.migrations.serializer import BaseSerializer, Serializer
from django.db.models import JSONField
from django.db.models.expressions import BaseExpression
from django.db.models.sql.compiler import SQLInsertCompiler
from django.db.models.sql.where import NothingNode
from django.utils.translation import get_language
Expand Down Expand Up @@ -248,6 +249,8 @@ def _parse(self, value, lang, use_nulls):
ret = value.raw_value
elif isinstance(value, dict):
ret = value
elif isinstance(value, BaseExpression):
raise NotImplementedError("Bulk updates are not supported for I18n_JSON fields.")
self.raw_value = ret

if "i18n_properties" in self.raw_value:
Expand Down
114 changes: 114 additions & 0 deletions arches/app/models/migrations/10541_controlled_list_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Generated by Django 4.2.9 on 2024-01-19 12:11

import uuid

import django.db.models.deletion
from django.db import migrations, models

from arches.app.models.fields.i18n import I18n_String

def add_plugins(apps, schema_editor):
Plugin = apps.get_model("models", "Plugin")

Plugin(
pluginid="29321ce0-bd95-4357-a2a5-822e9cb06f70",
name=I18n_String("Controlled List Manager"),
icon="fa fa-code-fork",
component="views/components/plugins/controlled-list-manager",
componentname="controlled-list-manager",
config={"show": True},
slug="controlled-list-manager",
sortorder=0,
).save()


def remove_plugin(apps, schema_editor):
Plugin = apps.get_model("models", "Plugin")

Plugin.objects.filter(slug="controlled-list-manager").delete()


class Migration(migrations.Migration):
dependencies = [
("models", "10515_i18n_plugins"),
]

operations = [
migrations.RunPython(add_plugins, remove_plugin),
migrations.CreateModel(
name="ControlledList",
fields=[
("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
("name", models.CharField(max_length=127)),
("dynamic", models.BooleanField(default=False)),
],
options={
"db_table": "controlled_lists",
},
),
migrations.CreateModel(
name="ControlledListItem",
fields=[
("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
("uri", models.URLField(max_length=2048, null=True, unique=True)),
(
"list",
models.ForeignKey(
db_column="listid", on_delete=django.db.models.deletion.CASCADE, related_name="items", to="models.controlledlist"
),
),
(
"parent",
models.ForeignKey(
null=True, on_delete=django.db.models.deletion.CASCADE, related_name="children", to="models.controlledlistitem"
),
),
],
options={
"db_table": "controlled_list_items",
},
),
migrations.CreateModel(
name="Label",
fields=[
("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
("value", models.CharField(max_length=1024)),
(
"item",
models.ForeignKey(
db_column="itemid",
on_delete=django.db.models.deletion.CASCADE,
related_name="labels",
to="models.controlledlistitem",
),
),
(
"language",
models.ForeignKey(
db_column="languageid", on_delete=django.db.models.deletion.PROTECT, to="models.language", to_field="code"
),
),
(
"value_type",
models.ForeignKey(
limit_choices_to={"category": "label"}, on_delete=django.db.models.deletion.PROTECT, to="models.dvaluetype"
),
),
],
options={
"db_table": "controlled_list_labels",
},
),
migrations.AddConstraint(
model_name="label",
constraint=models.UniqueConstraint(
fields=("item", "value", "value_type", "language"), name="unique_item_value_valuetype_language"
),
),
migrations.AddConstraint(
model_name="label",
constraint=models.UniqueConstraint(
condition=models.Q(("value_type", "prefLabel")), fields=("item", "language"), name="unique_item_preflabel_language"
),
),
]
62 changes: 62 additions & 0 deletions arches/app/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1835,3 +1835,65 @@ def __str__(self):
class Meta:
managed = True
db_table = "spatial_views"


### Controlled List Manager
class ControlledList(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=127, null=False)
dynamic = models.BooleanField(default=False)

class Meta:
db_table = "controlled_lists"


class ControlledListItem(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
# TODO(?): expose schemes
uri = models.URLField(max_length=2048, null=True, unique=True)
list = models.ForeignKey(
ControlledList,
db_column="listid",
on_delete=models.CASCADE,
related_name="items",
)
parent = models.ForeignKey(
"self", null=True, on_delete=models.CASCADE, related_name="children"
)

class Meta:
db_table = "controlled_list_items"


class Label(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
item = models.ForeignKey(
ControlledListItem,
db_column="itemid",
on_delete=models.CASCADE,
related_name="labels",
)
value_type = models.ForeignKey(
DValueType, on_delete=models.PROTECT, limit_choices_to={"category": "label"}
)
language = models.ForeignKey(
Language,
db_column="languageid",
to_field="code",
on_delete=models.PROTECT,
)
value = models.CharField(max_length=1024, null=False)

class Meta:
db_table = "controlled_list_labels"
constraints = [
models.UniqueConstraint(
fields=["item", "value", "value_type", "language"],
name="unique_item_value_valuetype_language",
),
models.UniqueConstraint(
fields=["item", "language"],
condition=Q(value_type="prefLabel"),
name="unique_item_preflabel_language",
),
]
13 changes: 13 additions & 0 deletions arches/app/src/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script setup>
import ControlledListManager from "@/components/ControlledListManager/ControlledListManager.vue";
</script>

<template>
<ControlledListManager />
</template>

<style>
div {
font-family: "Open Sans";
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<script setup>
import { ref } from "vue";
import Splitter from "primevue/splitter";
import SplitterPanel from "primevue/splitterpanel";

import ControlledListSidebar from "./ControlledListSidebar.vue";
import ControlledListTable from "./ControlledListTable.vue";

const lightGray = "#f4f4f4";

const displayedList = ref({});
</script>

<template>
<Splitter
:pt="{
gutter: { style: { background: lightGray } },
gutterHandler: { style: { background: lightGray } },
}"
>
<SplitterPanel :size="30" :minSize="15">
<ControlledListSidebar :displayedList="displayedList" />
</SplitterPanel>

<SplitterPanel :size="75" :minSize="50" class="mt-0">
<ControlledListTable :displayedList="displayedList" />
</SplitterPanel>
</Splitter>
</template>

<style scoped>
.p-splitter {
width: calc(100vw - 50px);
height: 100vh;
background: white;
font-size: 14px;
}
.p-splitter-panel {
display: flex;
flex-direction: column;
}
</style>
Loading