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

7.6.x -> 8.0.x #10692

Merged
merged 46 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
66374c6
Add a generic etl celery task
njkim Feb 1, 2024
de27796
Add generic task to run any function, #10573
njkim Feb 2, 2024
55f0ddf
prevent long titles from being hidden #10605
johnatawnclementawn Feb 12, 2024
4f1286e
Merge pull request #10606 from archesproject/10605_fix_resource_edito…
aarongundel Feb 14, 2024
b8188c3
Avoid logging exceptions for tiles holding only None
jacobtylerwalls Feb 13, 2024
39adac3
Remove duplicative release note
jacobtylerwalls Feb 13, 2024
ed58c83
Update 7.5 breaking changes with `<select>` admonition
jacobtylerwalls Feb 15, 2024
8559cd9
Add clarifying comment about aria-labels #10611
johnatawnclementawn Feb 15, 2024
10fdc68
Merge pull request #10611 from archesproject/select-admonition
johnatawnclementawn Feb 15, 2024
222401e
Detect extension locations from arches applications #10288
jacobtylerwalls Feb 16, 2024
d0d7a66
nit re #10288
jacobtylerwalls Feb 16, 2024
65ec49d
nit re #10288
jacobtylerwalls Feb 21, 2024
776c7f0
Merge pull request #10574 from archesproject/10573_add_generic_etl_ce…
apeters Feb 23, 2024
f22a0e0
Include .ts files in gettext extraction
jacobtylerwalls Feb 26, 2024
4951c84
Harden ResourceReport.get() against null sortorder
jacobtylerwalls Feb 28, 2024
604032f
fix bug with creating json-ld with url datatypes, re #9190
apeters Mar 2, 2024
dbb8f0d
Update setup.py template for 7.6
jacobtylerwalls Feb 1, 2024
6b1a0d9
typescript -> ts in 7.6.0 release notes
jacobtylerwalls Feb 1, 2024
0644544
Mention django-compressor removal
jacobtylerwalls Mar 1, 2024
4863675
Merge pull request #10570 from archesproject/setup-template-redux
chrabyrd Mar 4, 2024
f3e2307
Add regression test for #9198
jacobtylerwalls Mar 5, 2024
80ad6a6
nit re #9198 -- update now that both nodes in descriptor
jacobtylerwalls Mar 5, 2024
7375d08
Merge pull request #10654 from archesproject/9198-add-regression-test
apeters Mar 5, 2024
3e4a9c2
Add raise_exception arg to @group_required #10658
jacobtylerwalls Jan 18, 2024
4c7a463
Use `@group_required` in workflow history view re #10658
jacobtylerwalls Mar 6, 2024
7e9e707
test nit re #10658
jacobtylerwalls Mar 6, 2024
8713edf
Bump Django to 4.2.11
jacobtylerwalls Mar 4, 2024
02ed53c
Merge pull request #10613 from archesproject/detect-extensions-in-arc…
robgaston Mar 6, 2024
b5a5cb6
Merge pull request #10657 from archesproject/9190_url_json_ld_fix
jacobtylerwalls Mar 6, 2024
a70f149
Merge pull request #10636 from archesproject/attrgetter-failure
whatisgalen Mar 8, 2024
abd8de9
fix node sortoder on graph import, re #10668
apeters Mar 8, 2024
3dba99b
Merge pull request #10673 from archesproject/dev/7.5.x
njkim Mar 12, 2024
86e4bca
Bump vue to 3.4.21
jacobtylerwalls Mar 12, 2024
17aac35
Merge pull request #10674 from archesproject/vue-34
robgaston Mar 12, 2024
35c33dc
Downgrade openpyxl for excel importer/exporter compatiblity, #10614
njkim Mar 12, 2024
2c8c600
updates webpack CSS #10679
chrabyrd Mar 13, 2024
2312329
Bump django-silk to 5.1.0
jacobtylerwalls Mar 13, 2024
8a179a1
fixes index page regression #10627 (#10686)
chrabyrd Mar 14, 2024
e840d53
Add test command re #10658
jacobtylerwalls Mar 14, 2024
45a8cc2
Bump primevue to 3.50.0 (#10689)
jacobtylerwalls Mar 15, 2024
0482630
Merge pull request #10632 from archesproject/include-ts-in-gettext
chrabyrd Mar 15, 2024
456032e
rough out plugin-standalone logic #10672 (#10678)
chrabyrd Mar 15, 2024
440648e
updates dependencies
aarongundel Mar 18, 2024
19f0b29
updates release notes
aarongundel Mar 18, 2024
5456be6
unnecessary quotes
aarongundel Mar 18, 2024
b5d64c0
Merge pull request #10659 from archesproject/raise-exception-arg
apeters Mar 19, 2024
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
7 changes: 7 additions & 0 deletions arches/app/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ class IntegrityCheck(Enum):

def __str__(self):
return IntegrityCheckDescriptions[self.value]


class ExtensionType(Enum):
DATATYPES = "datatypes"
ETL_MODULES = "etl_modules"
FUNCTIONS = "functions"
SEARCH_COMPONENTS = "search_components"
2 changes: 1 addition & 1 deletion arches/app/datatypes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def get_tile_data(self, tile):
logger.warning(_("Multiple provisional edits. Returning first edit"))
userid = list(provisionaledits.keys())[0]
return provisionaledits[userid]["value"]
else:
elif not data:
logger.exception(_("Tile has no authoritative or provisional data"))


Expand Down
4 changes: 3 additions & 1 deletion arches/app/datatypes/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

from django.core.files.images import get_image_dimensions
from django.db.models import fields

from arches.app.const import ExtensionType
from arches.app.datatypes.base import BaseDataType
from arches.app.models import models
from arches.app.models.system_settings import settings
Expand Down Expand Up @@ -98,7 +100,7 @@ def get_instance(self, datatype):
try:
datatype_instance = DataTypeFactory._datatype_instances[d_datatype.classname]
except KeyError:
class_method = get_class_from_modulename(d_datatype.modulename, d_datatype.classname, settings.DATATYPE_LOCATIONS)
class_method = get_class_from_modulename(d_datatype.modulename, d_datatype.classname, ExtensionType.DATATYPES)
datatype_instance = class_method(d_datatype)
DataTypeFactory._datatype_instances[d_datatype.classname] = datatype_instance
self.datatype_instances = DataTypeFactory._datatype_instances
Expand Down
4 changes: 3 additions & 1 deletion arches/app/datatypes/url.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@ def append_search_filters(self, value, node, query, request):
pass

def get_rdf_uri(self, node, data, which="r"):
return URIRef(data["url"])
if data and "url" in data:
return URIRef(data["url"])
return None

def accepts_rdf_uri(self, uri):
return self.URL_REGEX.match(uri) and not (
Expand Down
2 changes: 1 addition & 1 deletion arches/app/media/css/arches.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5571,12 +5571,12 @@ div.card-grid-item.selected {
width: 100%;
height: 50px;
overflow: hidden;
display: flex;
}

.ep-graph-title {
font-size: 1.6rem;
padding: 5px;
flex-wrap: wrap;
align-items: center;
}

Expand Down
2 changes: 1 addition & 1 deletion arches/app/media/js/utils/create-vue-application.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Tooltip from 'primevue/tooltip';
import { createApp } from 'vue';
import { createGettext } from "vue3-gettext";

export default async function createVueApp(vueComponent){
export default async function createVueApplication(vueComponent){
/**
* This wrapper allows us to maintain a level of control inside arches-core
* over Vue apps. For instance this allows us to abstract i18n setup/config
Expand Down
15 changes: 15 additions & 0 deletions arches/app/media/js/views/plugin-standalone.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { defineAsyncComponent } from 'vue';
import createVueApplication from 'utils/create-vue-application';

const pluginData = require('views/plugin-data');

// workaround for webpack failures surrounding dynamic imports
const vuePluginPath = pluginData['component'].replace('src/', '').replace('.vue', '');
const AsyncComponent = defineAsyncComponent(() => import(`@/${vuePluginPath}.vue`));

const pluginMountingPoint = document.querySelector('#plugin-mounting-point');
pluginMountingPoint.setAttribute("id", pluginData['slug']);

createVueApplication(AsyncComponent).then(vueApp => {
vueApp.mount(`#${pluginData['slug']}`);
});
Original file line number Diff line number Diff line change
Expand Up @@ -1703,7 +1703,7 @@
container.find('.tp-caption').each(function() { jQuery(this).addClass(jQuery(this).data('transition')); jQuery(this).addClass('start') });

// PREPARE THE UL CONTAINER TO HAVEING MAX HEIGHT AND HEIGHT FOR ANY SITUATION
container.find('>ul:first').css({overflow:'hidden',width:'100%',height:'100%',maxHeight:container.parent().css('maxHeight')}).addClass("tp-revslider-mainul");
container.find('>ul:first').css({overflow:'hidden',width:'100%',height:'100%',padding:'0px',maxHeight:container.parent().css('maxHeight')}).addClass("tp-revslider-mainul");
if (opt.autoHeight=="on") {
container.find('>ul:first').css({overflow:'hidden',width:'100%',height:'100%',maxHeight:"none"});
container.css({'maxHeight':'none'});
Expand Down

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions arches/app/models/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ def add_node(self, node, nodegroups=None):
node.issearchable = nodeobj.get("issearchable", True)
node.isrequired = nodeobj.get("isrequired", False)
node.exportable = nodeobj.get("exportable", False)
node.sortorder = nodeobj.get("sortorder", 0)
node.fieldname = nodeobj.get("fieldname", "")
node.hascustomalias = nodeobj.get("hascustomalias", False)
node.sourcebranchpublication_id = nodeobj.get("sourcebranchpublication_id", None)
Expand Down
7 changes: 4 additions & 3 deletions arches/app/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import traceback
import django.utils.timezone

from arches.app.const import ExtensionType
from arches.app.utils.module_importer import get_class_from_modulename
from arches.app.utils.thumbnail_factory import ThumbnailGeneratorInstance
from arches.app.models.fields.i18n import I18n_TextField, I18n_JSONField
Expand Down Expand Up @@ -460,7 +461,7 @@ def defaultconfig_json(self):
return json_string

def get_class_module(self):
return get_class_from_modulename(self.modulename, self.classname, settings.FUNCTION_LOCATIONS)
return get_class_from_modulename(self.modulename, self.classname, ExtensionType.FUNCTIONS)


class FunctionXGraph(models.Model):
Expand Down Expand Up @@ -1045,7 +1046,7 @@ class Meta:
db_table = "search_component"

def get_class_module(self):
return get_class_from_modulename(self.modulename, self.classname, settings.SEARCH_COMPONENT_LOCATIONS)
return get_class_from_modulename(self.modulename, self.classname, ExtensionType.SEARCH_COMPONENTS)

def toJSON(self):
from arches.app.utils.betterJSONSerializer import JSONSerializer, JSONDeserializer
Expand Down Expand Up @@ -1752,7 +1753,7 @@ class Meta:
db_table = "etl_modules"

def get_class_module(self):
return get_class_from_modulename(self.modulename, self.classname, settings.ETL_MODULE_LOCATIONS)
return get_class_from_modulename(self.modulename, self.classname, ExtensionType.ETL_MODULES)


class LoadEvent(models.Model):
Expand Down
3 changes: 2 additions & 1 deletion arches/app/search/components/base.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from arches.app.const import ExtensionType
from arches.app.models import models
from arches.app.models.system_settings import settings
from arches.app.utils.module_importer import get_class_from_modulename
Expand Down Expand Up @@ -60,7 +61,7 @@ def get_filter(self, componentname):
except:
filter_instance = None
class_method = get_class_from_modulename(
search_filter.modulename, search_filter.classname, settings.SEARCH_COMPONENT_LOCATIONS
search_filter.modulename, search_filter.classname, ExtensionType.SEARCH_COMPONENTS
)
if class_method:
filter_instance = class_method(self.request)
Expand Down
48 changes: 48 additions & 0 deletions arches/app/tasks.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import importlib
import os
import logging
import shutil
Expand Down Expand Up @@ -352,6 +353,53 @@ def bulk_data_deletion(userid, load_id, graph_id, nodegroup_id, resourceids):
user = User.objects.get(id=userid)
notify_completion(msg, user)


@shared_task
def run_task(module_name=None, class_name=None, method_to_run=None, **kwargs):
"""
this allows the user to run any method as a celery task
module_name, class_name, and method_to_run are required
pass any additional arguments to the method via the kwargs parameter
"""

theClass = getattr(importlib.import_module(module_name), class_name)
theMethod = getattr(theClass(), method_to_run)
theMethod(**kwargs)


@shared_task
def run_etl_task(**kwargs):
"""
this allows the user to run the custom etl module
import_module, import_class, loadid, userid are the required string parameter
importer_name can be added (not required) for messaging purpose
"""

logger = logging.getLogger(__name__)

import_module = kwargs.pop("import_module")
import_class = kwargs.pop("import_class")
importer_name = kwargs.pop("importer_name", import_class)
loadid = kwargs.get("loadid")
userid = kwargs.get("userid")

try:
run_task(module_name=import_module, class_name=import_class, method_to_run="run_load_task", **kwargs)

load_event = models.LoadEvent.objects.get(loadid=loadid)
status = _("Completed") if load_event.status == "indexed" else _("Failed")
except Exception as e:
logger.error(e)
load_event = models.LoadEvent.objects.get(loadid=loadid)
load_event.status = "failed"
load_event.save()
status = _("Failed")
finally:
msg = _("{}: {}").format(importer_name, status)
user = User.objects.get(id=userid)
notify_completion(msg, user)


@shared_task
def reverse_etl_load(loadid):
from arches.app.etl_modules import base_import_module
Expand Down
2 changes: 1 addition & 1 deletion arches/app/templates/base-manager.htm
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ <h1 class="brand-text" style="margin-top: 15px;">{{ app_name }}</h1>

{% for p in plugins %}
{% if p.config is not None %}
{% if p.config.show is not False %}
{% if p.config.show and not p.config.is_standalone %}
<!-- ko let: {uid: Math.random().toString()} -->
<li {% if main_script == "views/plugin" and plugin.pluginid == p.pluginid %} class="active-sub" {% endif %}>
{% if p.slug is not None %}
Expand Down
86 changes: 86 additions & 0 deletions arches/app/templates/views/plugin-standalone.htm
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<!--
ARCHES - a program developed to inventory and manage immovable cultural heritage.
Copyright (C) 2013 J. Paul Getty Trust and World Monuments Fund

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
{% load static %}
{% load webpack_static from webpack_loader %}


<!DOCTYPE html>
<!--[if IE 8]> <html lang="en" class="ie8"> <![endif]-->
<!--[if IE 9]> <html lang="en" class="ie9"> <![endif]-->
<!--[if !IE]><!--> <html lang="en"> <!--<![endif]-->

<head>
<title>
{% block title %} {{ plugin.name }} {% endblock title %}
</title>

<!-- Meta -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">

<link rel="shortcut icon" href="{% webpack_static 'favicons/favicon.ico' %}" type="image/x-icon" />
<link rel="apple-touch-icon" href="{% webpack_static 'favicons/apple-touch-icon.png' %}" />
<link rel="apple-touch-icon" sizes="76x76" href="{% webpack_static 'favicons/apple-touch-icon-76x76.png' %}" />
<link rel="apple-touch-icon" sizes="120x120" href="{% webpack_static 'favicons/apple-touch-icon-120x120.png' %}" />
<link rel="apple-touch-icon" sizes="152x152" href="{% webpack_static 'favicons/apple-touch-icon-152x152.png' %}" />
<link rel="apple-touch-icon" sizes="180x180" href="{% webpack_static 'favicons/apple-touch-icon-180x180.png' %}" />

{% block css %}
<!-- POC rough out of PrimeVue theme switcher -->
<link rel="stylesheet" href="{% webpack_static 'node_modules/primevue/resources/themes/mdc-light-indigo/theme.css' %}" />
<link rel="stylesheet" href="{% webpack_static 'node_modules/font-awesome/css/font-awesome.min.css' %}" />
{% endblock css %}
</head>

<body>
{% block main_content %}
<div id="plugin-mounting-point"></div>
{% endblock main_content %}
</body>

{% block javascript %}
<script src="{% webpack_static 'node_modules/requirejs/require.js' %}"></script>

{% block pre_require_js %}
<div
id="pluginData"
style="display: none;"
pluginData='{{ plugin_json }}'
></div>
{% endblock pre_require_js %}

{% if main_script %}
<script src="{% webpack_static '' %}build/js/{{main_script}}.js"></script>
{% endif %}

{% if app_settings.GOOGLE_ANALYTICS_TRACKING_ID != None %}
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', '{{app_settings.GOOGLE_ANALYTICS_TRACKING_ID}}', 'auto');
ga('send', 'pageview');
</script>
{% endif %}
{% endblock javascript %}

</html>
5 changes: 4 additions & 1 deletion arches/app/utils/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def new_func(*args, **kwargs):
return new_func


def group_required(*group_names):
def group_required(*group_names, raise_exception=False):
"""
Requires user membership in at least one of the groups passed in.

Expand All @@ -69,6 +69,9 @@ def in_groups(u):
if u.is_authenticated:
if u.is_superuser or bool(u.groups.filter(name__in=group_names)):
return True
if raise_exception:
raise PermissionDenied
# else: user_passes_test() redirects to nowhere
return False

return user_passes_test(in_groups)
Expand Down
26 changes: 24 additions & 2 deletions arches/app/utils/module_importer.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
import importlib

from arches.app.const import ExtensionType
from arches.app.models.system_settings import settings


def get_module(path, modulename=""):
module = importlib.machinery.SourceFileLoader(modulename, path).load_module()
return module


def get_class_from_modulename(modulename, classname, directory_list):
def get_directories(extension_type: ExtensionType):
core_root_dir = f"arches.app.{extension_type.value}"
if extension_type is ExtensionType.SEARCH_COMPONENTS:
core_root_dir = core_root_dir.replace("search_components", "search.components")

core_and_arches_app_dirs = [core_root_dir]
for arches_app in settings.ARCHES_APPLICATIONS:
core_and_arches_app_dirs.append(f"{arches_app}.{extension_type.value}")
core_and_arches_app_dirs.append(f"{arches_app}.pkg.extensions.{extension_type.value}")

filtered_settings_dirs = [
setting_dir for setting_dir in
getattr(settings, extension_type.value.upper()[:-1] + "_LOCATIONS")
if setting_dir not in core_and_arches_app_dirs
]

return core_and_arches_app_dirs + filtered_settings_dirs


def get_class_from_modulename(modulename, classname, extension_type: ExtensionType):
mod_path = modulename.replace(".py", "")
module = None
import_success = False
import_error = None
for directory in directory_list:
for directory in get_directories(extension_type):
try:
module = importlib.import_module(directory + ".%s" % mod_path)
import_success = True
Expand Down
6 changes: 4 additions & 2 deletions arches/app/views/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from rdflib.namespace import SKOS, DCTERMS
from revproxy.views import ProxyView
from slugify import slugify
from operator import attrgetter
from urllib import parse
from collections import OrderedDict
from django.contrib.auth import authenticate
Expand Down Expand Up @@ -1155,7 +1154,10 @@ def get(self, request, resourceid):

cardwidgets = [
widget
for widgets in [sorted(card.cardxnodexwidget_set.all(), key=attrgetter("sortorder")) for card in permitted_cards]
for widgets in [
sorted(card.cardxnodexwidget_set.all(), key=lambda x: x.sortorder or 0)
for card in permitted_cards
]
for widget in widgets
]

Expand Down
Loading