From 0d3def4b020694c468f787e1b363dc7b3e1817c6 Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Thu, 21 Nov 2024 16:48:14 -0800 Subject: [PATCH 01/10] SND/Packages schema --- api-src/org/labkey/api/snd/SNDDomainKind.java | 24 +++ src/org/labkey/snd/PackageUserSchema.java | 186 ++++++++++++++++++ src/org/labkey/snd/SNDManager.java | 1 + src/org/labkey/snd/SNDUserSchema.java | 19 ++ .../labkey/snd/query/AttributeDataTable.java | 8 +- src/org/labkey/snd/query/EventDataTable.java | 5 +- src/org/labkey/snd/query/LookupsTable.java | 10 +- 7 files changed, 238 insertions(+), 15 deletions(-) create mode 100644 src/org/labkey/snd/PackageUserSchema.java diff --git a/api-src/org/labkey/api/snd/SNDDomainKind.java b/api-src/org/labkey/api/snd/SNDDomainKind.java index d5c46d90..d7c57a07 100644 --- a/api-src/org/labkey/api/snd/SNDDomainKind.java +++ b/api-src/org/labkey/api/snd/SNDDomainKind.java @@ -15,12 +15,17 @@ */ package org.labkey.api.snd; +import org.labkey.api.data.CompareType; import org.labkey.api.data.Container; +import org.labkey.api.data.CoreSchema; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.dialect.SqlDialect; import org.labkey.api.exp.property.Domain; import org.labkey.api.gwt.client.model.GWTPropertyDescriptor; import org.labkey.api.query.ExtendedTableDomainKind; import org.labkey.api.security.User; import org.labkey.api.security.permissions.AdminPermission; +import org.labkey.api.settings.AppProps; import org.labkey.data.xml.domainTemplate.DomainTemplateType; import org.labkey.data.xml.domainTemplate.SNDTemplateType; @@ -89,4 +94,23 @@ public boolean matchesTemplateXML(String templateName, DomainTemplateType templa { return template instanceof SNDTemplateType; } + + + public static String formatSndDomainURI(int containerRowId, int packageId) + { + return String.format("urn:lsid:%s:package-snd.Folder-%d:Package-%d", + AppProps.getInstance().getDefaultLsidAuthority(), + containerRowId, + packageId); + } + + public static SQLFragment likeSndDomainURI(Integer containerRowId, Integer packageId) + { + String pattern = String.format("urn:lsid:%s:package-snd.Folder-%s:Package-%s", + CompareType.escapeLikePattern(AppProps.getInstance().getDefaultLsidAuthority(), '!'), + null==containerRowId ? "%" : Integer.toString(containerRowId), + null==packageId ? "%" : Integer.toString(packageId) + ); + return new SQLFragment("LIKE ").appendValue(pattern).append(" ESCAPE '!'"); + } } diff --git a/src/org/labkey/snd/PackageUserSchema.java b/src/org/labkey/snd/PackageUserSchema.java new file mode 100644 index 00000000..932debb6 --- /dev/null +++ b/src/org/labkey/snd/PackageUserSchema.java @@ -0,0 +1,186 @@ +package org.labkey.snd; + +import org.jetbrains.annotations.Nullable; +import org.labkey.api.collections.CaseInsensitiveHashSet; +import org.labkey.api.collections.CaseInsensitiveTreeSet; +import org.labkey.api.data.ColumnInfo; +import org.labkey.api.data.Container; +import org.labkey.api.data.ContainerFilter; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SimpleFilter; +import org.labkey.api.data.SqlSelector; +import org.labkey.api.data.TableInfo; +import org.labkey.api.exp.PropertyColumn; +import org.labkey.api.exp.property.Domain; +import org.labkey.api.exp.property.DomainProperty; +import org.labkey.api.exp.property.PropertyService; +import org.labkey.api.query.FilteredTable; +import org.labkey.api.query.SchemaKey; +import org.labkey.api.query.UserSchema; +import org.labkey.api.security.User; +import org.labkey.api.snd.SNDDomainKind; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Consumer; + +public class PackageUserSchema extends UserSchema +{ + public static final String SCHEMA_NAME = "Packages"; + + final SNDUserSchema _snd; + + public PackageUserSchema(SNDUserSchema parent) + { + super(new SchemaKey(parent.getSchemaPath(), SCHEMA_NAME), null, parent.getUser(), parent.getContainer(), parent.getDbSchema(), null); + _snd = parent; + } + + @Override + public Set getSchemaNames() + { + return Set.of(); + } + + @Override + public Set getTableNames() + { + List list = new ArrayList<>(); + visitAll(p -> list.add(p.description)); + + // check for duplicates + Set duplicates = new CaseInsensitiveHashSet(); + Set ret = new CaseInsensitiveTreeSet(); + for (String s : list) + if (!ret.add(s)) + duplicates.add(s); + ret.removeAll(duplicates); + + return ret; + } + + @Override + public @Nullable TableInfo createTable(String name, ContainerFilter cf) + { + return createPackageTable(name); + } + + TableInfo createPackageTable(String name) + { + // find domain for name + Map pkgs = new HashMap<>(); + visitAll(p -> { + if (name.equalsIgnoreCase(p.description)) + pkgs.put(p.packageId, p); + }); + if (1 != pkgs.size()) + return null; + var entry = pkgs.entrySet().iterator().next(); + int packageId = entry.getKey(); + String description = entry.getValue().description; + TableInfo eventData = _snd.getTable("EventData", null, true, true); + if (null == eventData) + return null; + return new PackageTableInfo(this, eventData, description, packageId); + } + + class PackageTableInfo extends FilteredTable + { + final int packageId; + + PackageTableInfo(PackageUserSchema schema, TableInfo eventData, String packageName, int packageId) + { + super(eventData, schema, null); + setName(packageName); + this.packageId = packageId; + + var me = getPackage(packageId); + if (null == me || me.superPkgIds.isEmpty()) + addCondition(new SimpleFilter(new SimpleFilter.SQLClause(new SQLFragment("(0=1)")))); + else + addInClause(eventData.getColumn("SuperPkgId"), me.superPkgIds); + } + + @Override + protected void initializeColumns() + { + wrapAllColumns(true); + + Package p = getPackage(packageId); + + Container c = getUserSchema().getContainer(); + User user = getUserSchema().getUser(); + ColumnInfo object = Objects.requireNonNull(getColumn("ObjectURI", false)); + if (p.domain != null) + { + for (DomainProperty dp : p.domain.getProperties()) + { + if (null == getColumn(dp.getName(), false)) + { + PropertyColumn column = new PropertyColumn(dp.getPropertyDescriptor(), object, c, user, false); + addColumn(column); + } + } + } + } + } + + + /* + * Package helpers + * CONSIDER: move to a cache in SNDManager + */ + + public record SuperPkg(int superPkgId, Integer parentSuperPkgId, int pkgId, String description) {} + public static class Package + { + Package(int packageId, String description, Domain d) + { + this.packageId = packageId; + this.description = description; + this.domain = d; + } + final int packageId; + final String description; + final Domain domain; + List superPkgIds = new ArrayList<>(); + } + Map packagesMap; + + void initPackages() + { + if (null == packagesMap) + { + List supers = new SqlSelector(getDbSchema(), new SQLFragment( + new SQLFragment("SELECT SuperPkgId, ParentSuperPkgId, Pkgs.PkgId, Description FROM snd.Pkgs INNER JOIN snd.SuperPkgs ON Pkgs.PkgId = SuperPkgs.PkgId WHERE Pkgs.Container = ").appendValue(getContainer()) + )).getArrayList(SuperPkg.class); + Map map = new HashMap<>(); + supers.forEach(superPkg -> { + var package_ = map.computeIfAbsent(superPkg.pkgId, id -> + { + String uri = SNDDomainKind.formatSndDomainURI(getContainer().getRowId(), superPkg.pkgId); + var domain = PropertyService.get().getDomain(getContainer(), uri); + return new Package(superPkg.pkgId, superPkg.description, domain); + }); + package_.superPkgIds.add(superPkg.superPkgId); + }); + packagesMap = map; + } + } + + Package getPackage(int id) + { + initPackages(); + return packagesMap.get(id); + } + + void visitAll(Consumer fn) + { + initPackages(); + packagesMap.values().forEach(fn); + } +} diff --git a/src/org/labkey/snd/SNDManager.java b/src/org/labkey/snd/SNDManager.java index a88df60b..bdd55f6f 100644 --- a/src/org/labkey/snd/SNDManager.java +++ b/src/org/labkey/snd/SNDManager.java @@ -68,6 +68,7 @@ import org.labkey.api.security.User; import org.labkey.api.security.roles.FolderAdminRole; import org.labkey.api.security.roles.RoleManager; +import org.labkey.api.settings.AppProps; import org.labkey.api.settings.LookAndFeelProperties; import org.labkey.api.snd.AttributeData; import org.labkey.api.snd.Category; diff --git a/src/org/labkey/snd/SNDUserSchema.java b/src/org/labkey/snd/SNDUserSchema.java index b4c9e458..d24e792b 100644 --- a/src/org/labkey/snd/SNDUserSchema.java +++ b/src/org/labkey/snd/SNDUserSchema.java @@ -26,6 +26,7 @@ import org.labkey.api.data.TableInfo; import org.labkey.api.data.TableSelector; import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QuerySchema; import org.labkey.api.query.SimpleUserSchema; import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; @@ -66,6 +67,24 @@ public SNDUserSchema(String name, @Nullable String description, User user, Conta _contextualRole = contextualRole; } + @Override + public Set getSchemaNames() + { + if (_restricted) + return Set.of(); + return Set.of(PackageUserSchema.SCHEMA_NAME); + } + + @Override + public QuerySchema getSchema(String name) + { + if (_restricted) + return null; + if ("Packages".equalsIgnoreCase(name)) + return new PackageUserSchema(this); + return super.getSchema(name); + } + @Override public @NotNull Set getContextualRoles() { diff --git a/src/org/labkey/snd/query/AttributeDataTable.java b/src/org/labkey/snd/query/AttributeDataTable.java index 4bb2d036..82aa483c 100644 --- a/src/org/labkey/snd/query/AttributeDataTable.java +++ b/src/org/labkey/snd/query/AttributeDataTable.java @@ -46,6 +46,7 @@ import org.labkey.api.security.UserPrincipal; import org.labkey.api.security.permissions.Permission; import org.labkey.api.settings.AppProps; +import org.labkey.api.snd.SNDDomainKind; import org.labkey.api.snd.SNDService; import org.labkey.api.util.UnexpectedException; import org.labkey.snd.SNDManager; @@ -122,16 +123,13 @@ public SQLFragment getFromSQL(String alias) sql.append(" INNER JOIN "); sql.append(OntologyManager.getTinfoPropertyDescriptor(), "pd"); // Filter to include only properties associated with packages - sql.append(" ON x.PropertyId = pd.PropertyId AND pd.PropertyURI LIKE ? INNER JOIN "); + sql.append(" ON x.PropertyId = pd.PropertyId AND pd.PropertyURI ").append(SNDDomainKind.likeSndDomainURI(null,null)).append(" INNER JOIN "); // Filter to include only values associated with EventDatas sql.append(SNDSchema.getInstance().getTableInfoEventData(), "ed"); sql.append(" ON ed.ObjectURI = o.ObjectURI "); sql.append(") "); sql.append(alias); - // Note - this must be kept in sync with the PropertyURIs generated for the packages - sql.add("urn:lsid:" + AppProps.getInstance().getDefaultLsidAuthority() + ":package-snd.Folder-%"); - return sql; } @@ -399,7 +397,7 @@ public int truncateRows(User user, Container container, @Nullable Map { @@ -71,13 +66,12 @@ public LookupsTable init() { isInUseQuery.append(" INNER JOIN "); isInUseQuery.append(OntologyManager.getTinfoPropertyDescriptor(), "pd"); isInUseQuery.append(" ON ls.SetName = pd.LookupQuery "); - isInUseQuery.append(" AND pd.PropertyURI LIKE ? "); + isInUseQuery.append(" AND pd.PropertyURI ").append(SNDDomainKind.likeSndDomainURI(null,null)); isInUseQuery.append(" INNER JOIN "); isInUseQuery.append(OntologyManager.getTinfoObjectProperty(), "op"); isInUseQuery.append(" ON op.PropertyId = pd.PropertyId "); isInUseQuery.append(" WHERE CAST(" + ExprColumn.STR_TABLE_ALIAS + ".LookupId AS FLOAT) = op.FloatValue) "); isInUseQuery.append(" THEN 'true' else 'false' END)"); - isInUseQuery.add("urn:lsid:" + AppProps.getInstance().getDefaultLsidAuthority() + ":package-snd.Folder-%"); ExprColumn isInUseColumn = new ExprColumn(this, "IsInUse", isInUseQuery, JdbcType.BOOLEAN); addColumn(isInUseColumn); From 734d07aea67f953a97bffcc18bf2ad130dc08def Mon Sep 17 00:00:00 2001 From: labkey-matthewb Date: Mon, 2 Dec 2024 14:38:30 -0800 Subject: [PATCH 02/10] SubjectId, Date, SequenceNum --- src/org/labkey/snd/PackageUserSchema.java | 45 +++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/org/labkey/snd/PackageUserSchema.java b/src/org/labkey/snd/PackageUserSchema.java index 932debb6..86d69ee3 100644 --- a/src/org/labkey/snd/PackageUserSchema.java +++ b/src/org/labkey/snd/PackageUserSchema.java @@ -1,26 +1,33 @@ package org.labkey.snd; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.labkey.api.collections.CaseInsensitiveHashSet; import org.labkey.api.collections.CaseInsensitiveTreeSet; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerFilter; +import org.labkey.api.data.JdbcType; import org.labkey.api.data.SQLFragment; import org.labkey.api.data.SimpleFilter; import org.labkey.api.data.SqlSelector; import org.labkey.api.data.TableInfo; +import org.labkey.api.data.dialect.SqlDialect; import org.labkey.api.exp.PropertyColumn; import org.labkey.api.exp.property.Domain; import org.labkey.api.exp.property.DomainProperty; import org.labkey.api.exp.property.PropertyService; +import org.labkey.api.query.AliasedColumn; +import org.labkey.api.query.ExprColumn; import org.labkey.api.query.FilteredTable; import org.labkey.api.query.SchemaKey; import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; import org.labkey.api.snd.SNDDomainKind; +import org.labkey.api.study.StudyService; import java.util.ArrayList; +import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -28,6 +35,8 @@ import java.util.Set; import java.util.function.Consumer; +import static org.labkey.api.query.ExprColumn.STR_TABLE_ALIAS; + public class PackageUserSchema extends UserSchema { public static final String SCHEMA_NAME = "Packages"; @@ -88,6 +97,7 @@ TableInfo createPackageTable(String name) return new PackageTableInfo(this, eventData, description, packageId); } + class PackageTableInfo extends FilteredTable { final int packageId; @@ -105,9 +115,32 @@ class PackageTableInfo extends FilteredTable addInClause(eventData.getColumn("SuperPkgId"), me.superPkgIds); } + /* TODO: duplicate code StudyUtils is not public (add to Study class?) */ + public static SQLFragment sequenceNumFromDateSQL(SQLFragment dateSql) + { + // SqlDialect.getDatePart() should not convert SQLFragment to String + if (!dateSql.getParams().isEmpty()) + throw new IllegalStateException(); + // Returns a SQL statement that produces a single number from a date, in the form of YYYYMMDD. + SqlDialect dialect = StudyService.get().getStudySchema().getSqlDialect(); + SQLFragment sql = new SQLFragment(); + sql.append("CAST((10000 * ").append(dialect.getDatePart(Calendar.YEAR, dateSql)).append(") + "); + sql.append("(100 * ").append(dialect.getDatePart(Calendar.MONTH, dateSql)).append(") + "); + sql.append("(").append(dialect.getDatePart(Calendar.DAY_OF_MONTH, dateSql)).append(") AS NUMERIC(15,4))"); + return sql; + } + @Override protected void initializeColumns() { + TableInfo events = getSchema().getTable("Events"); + + addColumn(new AliasedColumn(this, "SubjectId", events.getColumn("SubjectId"))); + addColumn(new AliasedColumn(this, "Date", events.getColumn("Date"))); + var date = new SQLFragment(events.getColumn("Date").getValueSql(STR_TABLE_ALIAS)); + var seqnum = sequenceNumFromDateSQL(date); + addColumn(new ExprColumn(this, "SequenceNum", seqnum, JdbcType.DECIMAL)); + wrapAllColumns(true); Package p = getPackage(packageId); @@ -127,6 +160,18 @@ protected void initializeColumns() } } } + + @Override + public @NotNull SQLFragment getFromSQL(String alias) + { + TableInfo events = getSchema().getTable("Events"); + return new SQLFragment("(SELECT events.SubjectId, events.Date, eventdata.*\n") + .append("FROM ").append(getFromTable().getFromSQL("eventdata")) + .append(" INNER JOIN ").append(events.getFromSQL("events")) + .append( " ON eventdata.eventid = events.eventid\n") + .append("WHERE events.Container=").appendValue(getContainer()) + .append(") ").append(alias).append("\n"); + } } From be8427eaaa2b41f137ca3da35a05abdd2c1c1f07 Mon Sep 17 00:00:00 2001 From: Matthew Bellew Date: Wed, 4 Dec 2024 14:27:32 -0800 Subject: [PATCH 03/10] EventData.objectid column --- src/org/labkey/snd/query/EventDataTable.java | 136 +++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/src/org/labkey/snd/query/EventDataTable.java b/src/org/labkey/snd/query/EventDataTable.java index f2c5de35..ca08e430 100644 --- a/src/org/labkey/snd/query/EventDataTable.java +++ b/src/org/labkey/snd/query/EventDataTable.java @@ -18,10 +18,12 @@ import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.labkey.api.data.BaseColumnInfo; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerFilter; import org.labkey.api.data.DbSchema; import org.labkey.api.data.DbScope; +import org.labkey.api.data.JdbcType; import org.labkey.api.data.SQLFragment; import org.labkey.api.data.SqlExecutor; import org.labkey.api.data.TableInfo; @@ -66,12 +68,146 @@ public EventDataTable(SNDUserSchema schema, TableInfo table, ContainerFilter cf) super(schema, table, cf); } + public void addColumns() + { + super.addColumns(); + + BaseColumnInfo objectid = new BaseColumnInfo("ObjectId", this, JdbcType.INTEGER) + { + @Override + public SQLFragment getValueSql(String tableAliasName) + { + return new SQLFragment(tableAliasName).append(".").append("ObjectId"); + } + }; + addColumn(objectid); + } + + @Override + public @NotNull SQLFragment getFromSQL(String alias) + { + SQLFragment table = super.getFromSQL("_evnt_data_"); + SQLFragment join = new SQLFragment("(SELECT _evnt_data_.*, ObjectId FROM ") + .append(table).append(" INNER JOIN exp.Object ON _evnt_data_.ObjectURI = Object.ObjectURI").append(") ").append(alias); + return join; + } + @Override public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class perm) { return getContainer().hasPermission(user, SNDViewerPermission.class, getUserSchema().getContextualRoles()); } +/* This code implements method ".attribtues()". Probably not useful given the packages schema. + Map attributes = null; + + Map getAttributes() + { + Set ambiguiousShortNames = new CaseInsensitiveHashSet(); + if (null == attributes) + { + // CONSIDER: cache for Packages? + Map packages = new HashMap<>(); + new SqlSelector(SNDSchema.getInstance().getSchema(), + new SQLFragment("SELECT PkgId, Description FROM snd.Pkgs WHERE Container = ").appendValue(getContainer()) + ).fillValueMap(packages); + + Map map = new CaseInsensitiveHashMap<>(); + UserSchema userSchema = getUserSchema(); + Container c = userSchema.getContainer(); + // urn:lsid:labkey.com:package-snd.Folder-13:Package-820 + List list = PropertyService.get().getDomains(userSchema.getContainer(), userSchema.getUser(), false) + .stream().filter(d -> d.getTypeURI().contains(":package-snd.Folder-" + c.getRowId() + ":Package-")) + .toList(); + + for (var d : list) + { + d.getProperties().forEach(dp -> + { + PropertyDescriptor pd = dp.getPropertyDescriptor(); + String uri = dp.getPropertyURI(); + String domainName = d.getName(); + if (domainName.startsWith("Package-")) + { + try + { + var id = Integer.parseInt(domainName.substring("Package-".length())); + var s = packages.get(id); + if (null != s) + domainName = s; + } + catch (NumberFormatException ignore) + { + // pass + } + } + String domainColumn = domainName + "." + dp.getName(); + String column = dp.getName(); + if (null != map.put(uri,pd)) + ambiguiousShortNames.add(uri); + if (null != map.put(domainColumn,pd)) + ambiguiousShortNames.add(domainColumn); + if (null != map.put(column,pd)) + ambiguiousShortNames.add(column); + }); + } + for (String name : ambiguiousShortNames) + map.remove(name); + attributes = Collections.unmodifiableMap(map); + } + return attributes; + } + + MethodInfo attributeMethod = new AbstractTableMethodInfo(JdbcType.OTHER) + { + @Override + public JdbcType getJdbcType(JdbcType[] args) + { + // UNDONE: would be nice to have the actual arguments to inspect + return super.getJdbcType(args); + } + + @Override + public SQLFragment getSQL(String tableAlias, DbSchema schema, SQLFragment[] arguments) + { + ColumnInfo objectId = getColumn("ObjectId"); + if (null == objectId || arguments.length != 1) + { + return new SQLFragment(" NULL "); + } + try + { + String attributeName = QueryService.get().toSimpleString(arguments[0]); + var attributes = getAttributes(); + var pd = attributes.get(attributeName); + if (null == pd) + return new SQLFragment(" 'not found' "); + PropertyColumn pc = new PropertyColumn(pd, objectId, getUserSchema().getContainer(), getUserSchema().getUser(), false); + pc.setParentIsObjectId(true); + return pc.getValueSql(tableAlias); + } + catch (IllegalArgumentException x) + { + throw new QueryParseException("Constant string literal expected for attribute() method", x, -1, -1); + } + } + }; + + @Override + public MethodInfo getMethod(String name) + { + if ("Attribute".equalsIgnoreCase(name)) + return attributeMethod; + return super.getMethod(name); + } + + @Override + public Set getMethodRequiredFieldKeys() + { + return Set.of(new FieldKey(null, "ObjectId")); + } +*/ + @Override public QueryUpdateService getUpdateService() { From 7ee024a5ffa8eeba86039c91011452056916aa03 Mon Sep 17 00:00:00 2001 From: Matthew Bellew Date: Wed, 4 Dec 2024 16:15:49 -0800 Subject: [PATCH 04/10] objectid FK --- src/org/labkey/snd/query/EventDataTable.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/snd/query/EventDataTable.java b/src/org/labkey/snd/query/EventDataTable.java index ca08e430..14fbf96f 100644 --- a/src/org/labkey/snd/query/EventDataTable.java +++ b/src/org/labkey/snd/query/EventDataTable.java @@ -70,8 +70,6 @@ public EventDataTable(SNDUserSchema schema, TableInfo table, ContainerFilter cf) public void addColumns() { - super.addColumns(); - BaseColumnInfo objectid = new BaseColumnInfo("ObjectId", this, JdbcType.INTEGER) { @Override @@ -80,7 +78,12 @@ public SQLFragment getValueSql(String tableAliasName) return new SQLFragment(tableAliasName).append(".").append("ObjectId"); } }; + objectid.setHidden(true); + objectid.setFk(new BaseColumnInfo.SchemaForeignKey(objectid, "exp", "Object", "ObjectId", false)); + // SimpleTableSchema.SimpleTable.wrapColumn() is weird. It calls addColumn() which is not the usual pattern. + fixupWrappedColumn(objectid, objectid); addColumn(objectid); + super.addColumns(); } @Override From 0128cb7b8675386595de14853de8345131fabb1b Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Thu, 5 Dec 2024 08:56:09 -0800 Subject: [PATCH 05/10] Filter on eventdata. Add qcstate. --- src/org/labkey/snd/PackageUserSchema.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/snd/PackageUserSchema.java b/src/org/labkey/snd/PackageUserSchema.java index 86d69ee3..fbfe6014 100644 --- a/src/org/labkey/snd/PackageUserSchema.java +++ b/src/org/labkey/snd/PackageUserSchema.java @@ -110,9 +110,9 @@ class PackageTableInfo extends FilteredTable var me = getPackage(packageId); if (null == me || me.superPkgIds.isEmpty()) - addCondition(new SimpleFilter(new SimpleFilter.SQLClause(new SQLFragment("(0=1)")))); + ((FilteredTable)getRealTable()).addCondition(new SimpleFilter(new SimpleFilter.SQLClause(new SQLFragment("(0=1)")))); else - addInClause(eventData.getColumn("SuperPkgId"), me.superPkgIds); + ((FilteredTable)getRealTable()).addInClause(eventData.getColumn("SuperPkgId"), me.superPkgIds); } /* TODO: duplicate code StudyUtils is not public (add to Study class?) */ @@ -137,6 +137,7 @@ protected void initializeColumns() addColumn(new AliasedColumn(this, "SubjectId", events.getColumn("SubjectId"))); addColumn(new AliasedColumn(this, "Date", events.getColumn("Date"))); + addColumn(new AliasedColumn(this, "QcState", events.getColumn("QcState"))); var date = new SQLFragment(events.getColumn("Date").getValueSql(STR_TABLE_ALIAS)); var seqnum = sequenceNumFromDateSQL(date); addColumn(new ExprColumn(this, "SequenceNum", seqnum, JdbcType.DECIMAL)); @@ -165,7 +166,7 @@ protected void initializeColumns() public @NotNull SQLFragment getFromSQL(String alias) { TableInfo events = getSchema().getTable("Events"); - return new SQLFragment("(SELECT events.SubjectId, events.Date, eventdata.*\n") + return new SQLFragment("(SELECT events.SubjectId, events.Date, events.QcState, eventdata.*\n") .append("FROM ").append(getFromTable().getFromSQL("eventdata")) .append(" INNER JOIN ").append(events.getFromSQL("events")) .append( " ON eventdata.eventid = events.eventid\n") From 795775d6963c1279ddc9ce5ae28eebb9f9dd1919 Mon Sep 17 00:00:00 2001 From: Matthew Bellew Date: Thu, 12 Dec 2024 14:39:10 -0800 Subject: [PATCH 06/10] Categories schema --- resources/schemas/snd.xml | 8 +- src/org/labkey/snd/PackageUserSchema.java | 256 ++++++++++++++++++++-- src/org/labkey/snd/SNDUserSchema.java | 6 +- 3 files changed, 242 insertions(+), 28 deletions(-) diff --git a/resources/schemas/snd.xml b/resources/schemas/snd.xml index e8628bb3..787f5e04 100644 --- a/resources/schemas/snd.xml +++ b/resources/schemas/snd.xml @@ -240,8 +240,12 @@ - - + + true + + + true + lsidtype true diff --git a/src/org/labkey/snd/PackageUserSchema.java b/src/org/labkey/snd/PackageUserSchema.java index fbfe6014..acbe472e 100644 --- a/src/org/labkey/snd/PackageUserSchema.java +++ b/src/org/labkey/snd/PackageUserSchema.java @@ -1,15 +1,19 @@ package org.labkey.snd; +import org.apache.commons.collections4.MultiValuedMap; +import org.apache.commons.collections4.multimap.AbstractMultiValuedMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.labkey.api.collections.CaseInsensitiveHashSet; import org.labkey.api.collections.CaseInsensitiveTreeSet; +import org.labkey.api.data.AbstractTableInfo; +import org.labkey.api.data.BaseColumnInfo; import org.labkey.api.data.ColumnInfo; import org.labkey.api.data.Container; import org.labkey.api.data.ContainerFilter; import org.labkey.api.data.JdbcType; +import org.labkey.api.data.MutableColumnInfo; import org.labkey.api.data.SQLFragment; -import org.labkey.api.data.SimpleFilter; import org.labkey.api.data.SqlSelector; import org.labkey.api.data.TableInfo; import org.labkey.api.data.dialect.SqlDialect; @@ -19,16 +23,21 @@ import org.labkey.api.exp.property.PropertyService; import org.labkey.api.query.AliasedColumn; import org.labkey.api.query.ExprColumn; -import org.labkey.api.query.FilteredTable; +import org.labkey.api.query.FieldKey; +import org.labkey.api.query.QueryException; +import org.labkey.api.query.QueryService; import org.labkey.api.query.SchemaKey; import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; import org.labkey.api.snd.SNDDomainKind; -import org.labkey.api.study.StudyService; +import org.labkey.api.sql.LabKeySql; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -59,7 +68,7 @@ public Set getSchemaNames() public Set getTableNames() { List list = new ArrayList<>(); - visitAll(p -> list.add(p.description)); + visitAll(p -> {if (!p.superPkgIds.isEmpty()) list.add(p.description);}); // check for duplicates Set duplicates = new CaseInsensitiveHashSet(); @@ -72,13 +81,15 @@ public Set getTableNames() return ret; } + @Override public @Nullable TableInfo createTable(String name, ContainerFilter cf) { return createPackageTable(name); } - TableInfo createPackageTable(String name) + + PackageTableInfo createPackageTable(String name) { // find domain for name Map pkgs = new HashMap<>(); @@ -98,31 +109,73 @@ TableInfo createPackageTable(String name) } - class PackageTableInfo extends FilteredTable + PackageTableInfo createPackageTable(Package pkg) + { + int packageId = pkg.packageId; + String description = pkg.description; + TableInfo eventData = _snd.getTable("EventData", null, true, true); + if (null == eventData) + return null; + return new PackageTableInfo(this, eventData, description, packageId); + } + + + abstract static class _AbstractTableInfo extends AbstractTableInfo + { + final @NotNull UserSchema userSchema; + + _AbstractTableInfo(@NotNull UserSchema schema, String name) + { + super(schema.getDbSchema(), name); + userSchema = schema; + } + + @Override + public @NotNull UserSchema getUserSchema() + { + return userSchema; + } + + public MutableColumnInfo addWrapColumn(TableInfo table, String name, ColumnInfo column) + { + assert column.getParentTable() == table : "Column is not from the same \"real\" table"; + BaseColumnInfo ret = new AliasedColumn(this, name, column); + ret.setHidden(column.isHidden()); + addColumn(ret); + return ret; + } + + public void addWrapAllColumns(TableInfo table) + { + for (var col : table.getColumns()) + { + addWrapColumn(table, col.getName(), col); + } + } + } + + + class PackageTableInfo extends _AbstractTableInfo { final int packageId; + final TableInfo eventData; PackageTableInfo(PackageUserSchema schema, TableInfo eventData, String packageName, int packageId) { - super(eventData, schema, null); + super(schema, packageName); setName(packageName); this.packageId = packageId; - - var me = getPackage(packageId); - if (null == me || me.superPkgIds.isEmpty()) - ((FilteredTable)getRealTable()).addCondition(new SimpleFilter(new SimpleFilter.SQLClause(new SQLFragment("(0=1)")))); - else - ((FilteredTable)getRealTable()).addInClause(eventData.getColumn("SuperPkgId"), me.superPkgIds); + this.eventData = eventData; } + /* TODO: duplicate code StudyUtils is not public (add to Study class?) */ - public static SQLFragment sequenceNumFromDateSQL(SQLFragment dateSql) + public static SQLFragment sequenceNumFromDateSQL(SqlDialect dialect, SQLFragment dateSql) { // SqlDialect.getDatePart() should not convert SQLFragment to String if (!dateSql.getParams().isEmpty()) throw new IllegalStateException(); // Returns a SQL statement that produces a single number from a date, in the form of YYYYMMDD. - SqlDialect dialect = StudyService.get().getStudySchema().getSqlDialect(); SQLFragment sql = new SQLFragment(); sql.append("CAST((10000 * ").append(dialect.getDatePart(Calendar.YEAR, dateSql)).append(") + "); sql.append("(100 * ").append(dialect.getDatePart(Calendar.MONTH, dateSql)).append(") + "); @@ -139,10 +192,12 @@ protected void initializeColumns() addColumn(new AliasedColumn(this, "Date", events.getColumn("Date"))); addColumn(new AliasedColumn(this, "QcState", events.getColumn("QcState"))); var date = new SQLFragment(events.getColumn("Date").getValueSql(STR_TABLE_ALIAS)); - var seqnum = sequenceNumFromDateSQL(date); - addColumn(new ExprColumn(this, "SequenceNum", seqnum, JdbcType.DECIMAL)); + var seqnumSQL = sequenceNumFromDateSQL(getDbSchema().getSqlDialect(), date); + var seqnumCol = new ExprColumn(this, "SequenceNum", seqnumSQL, JdbcType.DECIMAL); + seqnumCol.setHidden(true); + addColumn(seqnumCol); - wrapAllColumns(true); + addWrapAllColumns(eventData); Package p = getPackage(packageId); @@ -162,16 +217,48 @@ protected void initializeColumns() } } + static Set eventDataCols = CaseInsensitiveHashSet.of("subjectid", "date", "qcstate", "sequencenum"); + + @Override + protected SQLFragment getFromSQLExpanded(String alias, Set cols) + { + boolean hasEventDataColumn = cols.stream().anyMatch(fk -> eventDataCols.contains(fk.getParts().get(0))); + return getFromSQLJoin(alias, hasEventDataColumn); + } + @Override public @NotNull SQLFragment getFromSQL(String alias) { + return getFromSQLJoin(alias, true); + } + + @Override + public @NotNull SQLFragment getFromSQL() + { + throw new IllegalStateException(); + } + + public @NotNull SQLFragment getFromSQLJoin(String alias, boolean withEventJoin) + { + SqlDialect dialect = getDbSchema().getSqlDialect(); + var me = getPackage(packageId); TableInfo events = getSchema().getTable("Events"); - return new SQLFragment("(SELECT events.SubjectId, events.Date, events.QcState, eventdata.*\n") - .append("FROM ").append(getFromTable().getFromSQL("eventdata")) - .append(" INNER JOIN ").append(events.getFromSQL("events")) - .append( " ON eventdata.eventid = events.eventid\n") - .append("WHERE events.Container=").appendValue(getContainer()) - .append(") ").append(alias).append("\n"); + var fromSql = new SQLFragment("(SELECT "); + if (withEventJoin) + fromSql.append("events.SubjectId, events.Date, events.QcState, "); + fromSql.append("eventdata.*\n"); + fromSql.append("FROM ").append(eventData.getFromSQL("eventdata")); + if (withEventJoin) + fromSql.append(" INNER JOIN ").append(events.getFromSQL("events")).append( " ON eventdata.eventid = events.eventid\n"); + fromSql.append("WHERE eventdata.Container=").appendValue(getContainer()); + if (null == me || me.superPkgIds.isEmpty()) + fromSql.append(" AND (0=1)"); + else + { + fromSql.append(" AND SuperPkgId "); + dialect.appendInClauseSql(fromSql, me.superPkgIds); + } + return fromSql.append(") ").append(alias).append("\n"); } } @@ -229,4 +316,125 @@ void visitAll(Consumer fn) initPackages(); packagesMap.values().forEach(fn); } + + + TableInfo createCategoryTable(UserSchema categoriesSchema, int categoryId, String name) + { + // use getTableNames() as it does duplicate description detection + Set tableNames = new HashSet<>(getTableNames()); + // get a tableInfo for each packages in category + List pkgs = new SqlSelector(getDbSchema(), new SQLFragment("SELECT DISTINCT PkgId FROM snd.PkgCategoryJunction WHERE CategoryId=").appendValue(categoryId)) + .stream(Integer.class) + .map(I -> getPackage(I.intValue())) + .filter(pkg -> null != pkg && tableNames.contains(pkg.description)) + .map(this::createPackageTable) + .toList(); + if (pkgs.isEmpty()) + return null; + + // This is _almost_ ArrayListValuedHashMap, but I want to track the keyset insertion order. + MultiValuedMap map = new AbstractMultiValuedMap<>(new LinkedHashMap<>()) + { + @Override + protected Collection createCollection() + { + return new ArrayList<>(); + } + }; + for (TableInfo t : pkgs) + { + for (ColumnInfo col : t.getColumns()) + { + // CONSIDER: consider optimize the snd.evens join by hoisting it out of the UNION + // e.g. if (PackageTableInfo.eventDataCols.contains(col.getName())) continue; + map.put(col.getFieldKey(), col); + } + } + var keys = new HashSet<>(map.keySet()); + keys.forEach(key -> { + var coll = map.get(key); + if (coll.size() != pkgs.size()) + { + map.remove(key); + return; + } + var opt = coll.stream().findFirst(); + final var jdbcType = opt.isPresent() ? opt.get().getJdbcType() : JdbcType.OTHER; + var allMatch = coll.stream().allMatch(c -> jdbcType == c.getJdbcType()); + if (!allMatch) + map.remove(key); + }); + if (map.isEmpty()) + return null; + + return new CategoriesTable(categoriesSchema, categoryId, name, pkgs, map); + } + + public class CategoriesTable extends _AbstractTableInfo + { + final int categoryId; + final List pkgTables; + final MultiValuedMap columnMap; + TableInfo queryTableInfo = null; + + CategoriesTable(UserSchema categoriesSchema, int categoryId, String name, List pkgs, MultiValuedMap columnMap) + { + super(categoriesSchema, name); + this.categoryId = categoryId; + this.pkgTables = pkgs; + this.columnMap = columnMap; + } + + @Override + protected void initializeColumns() + { + var hidden = CaseInsensitiveHashSet.of("Container", "LSID", "ObjectId", "ObjectURI", "SequenceNum"); + + // It's unfortunate that SND doesn't share property descriptors, we have to UNION each "package" table to get the "category" table. + // this is LabKey SQL (not using SQLFragment for this) + StringBuilder lkUnionSQL = new StringBuilder(); + var unionAll = ""; + var colNames = columnMap.keySet().toArray(new FieldKey[0]); + for (PackageTableInfo pkgTable : pkgTables) + { + lkUnionSQL.append(unionAll); + unionAll = "\nUNION ALL\n"; + var comma = ""; + lkUnionSQL.append("SELECT "); + for (FieldKey colName : colNames) + { + lkUnionSQL.append(comma); + comma = ", "; + lkUnionSQL.append(LabKeySql.quoteIdentifier(colName.getName())); + // LabKey SQL unhides columns by default + if (hidden.contains(colName.getName())) + lkUnionSQL.append(" @hidden"); + } + lkUnionSQL.append("\nFROM ").append("Packages.").append(LabKeySql.quoteIdentifier(pkgTable.getName())); + } + + var qdef = QueryService.get().createQueryDef(getUser(), getContainer(), _snd, "_union_"); + qdef.setSql(lkUnionSQL.toString()); + List errors = new ArrayList<>(); + queryTableInfo = qdef.getTable(_snd, errors, false); + if (!errors.isEmpty()) + throw errors.get(0); + if (null == queryTableInfo) + throw new QueryException("Unexpected error compiling query"); + + addWrapAllColumns(queryTableInfo); + } + + @Override + public @NotNull SQLFragment getFromSQL(String alias) + { + return queryTableInfo.getFromSQL(alias); + } + + @Override + protected SQLFragment getFromSQL() + { + throw new IllegalStateException(); + } + } } diff --git a/src/org/labkey/snd/SNDUserSchema.java b/src/org/labkey/snd/SNDUserSchema.java index d24e792b..7708689e 100644 --- a/src/org/labkey/snd/SNDUserSchema.java +++ b/src/org/labkey/snd/SNDUserSchema.java @@ -72,7 +72,7 @@ public Set getSchemaNames() { if (_restricted) return Set.of(); - return Set.of(PackageUserSchema.SCHEMA_NAME); + return Set.of(PackageUserSchema.SCHEMA_NAME, CategoryUserSchema.SCHEMA_NAME); } @Override @@ -80,8 +80,10 @@ public QuerySchema getSchema(String name) { if (_restricted) return null; - if ("Packages".equalsIgnoreCase(name)) + if (PackageUserSchema.SCHEMA_NAME.equalsIgnoreCase(name)) return new PackageUserSchema(this); + if (CategoryUserSchema.SCHEMA_NAME.equalsIgnoreCase(name)) + return new CategoryUserSchema(this); return super.getSchema(name); } From c37decaaf74bb8f89e8ebb83d713dab28d18849d Mon Sep 17 00:00:00 2001 From: Matthew Bellew Date: Thu, 12 Dec 2024 14:39:29 -0800 Subject: [PATCH 07/10] Categories schema --- src/org/labkey/snd/CategoryUserSchema.java | 76 ++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 src/org/labkey/snd/CategoryUserSchema.java diff --git a/src/org/labkey/snd/CategoryUserSchema.java b/src/org/labkey/snd/CategoryUserSchema.java new file mode 100644 index 00000000..f1648836 --- /dev/null +++ b/src/org/labkey/snd/CategoryUserSchema.java @@ -0,0 +1,76 @@ +package org.labkey.snd; + +import org.jetbrains.annotations.Nullable; +import org.labkey.api.collections.CaseInsensitiveHashSet; +import org.labkey.api.collections.CaseInsensitiveTreeSet; +import org.labkey.api.data.ContainerFilter; +import org.labkey.api.data.SQLFragment; +import org.labkey.api.data.SqlSelector; +import org.labkey.api.data.TableInfo; +import org.labkey.api.query.QuerySchema; +import org.labkey.api.query.SchemaKey; +import org.labkey.api.query.UserSchema; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class CategoryUserSchema extends UserSchema +{ + public static final String SCHEMA_NAME = "Categories"; + + final SNDUserSchema _sndSchema; + final QuerySchema _packageSchema; + + public CategoryUserSchema(SNDUserSchema parent) + { + super(new SchemaKey(parent.getSchemaPath(), SCHEMA_NAME), null, parent.getUser(), parent.getContainer(), parent.getDbSchema(), null); + _sndSchema = parent; + _packageSchema = _sndSchema.getSchema(PackageUserSchema.SCHEMA_NAME); + } + + @Override + public Set getSchemaNames() + { + return Set.of(); + } + + @Override + public Set getTableNames() + { + var sql = new SQLFragment("SELECT Description FROM snd.PkgCategories WHERE Active=1 AND Container=").appendValue(getContainer()); + List list = new SqlSelector(getDbSchema(), sql).getArrayList(String.class); + + // check for duplicates + Set duplicates = new CaseInsensitiveHashSet(); + Set ret = new CaseInsensitiveTreeSet(); + for (String s : list) + if (!ret.add(s)) + duplicates.add(s); + ret.removeAll(duplicates); + + return ret; + } + + @Override + public @Nullable TableInfo createTable(String name, ContainerFilter cf) + { + var sql = new SQLFragment("SELECT CategoryId, Description FROM snd.PkgCategories WHERE Active=1 AND Container=").appendValue(getContainer()) + .append(" AND lower(description) = lower(").appendValue(name).append(")"); + Map[] rs = new SqlSelector(getDbSchema(), sql).getMapArray(); + if (rs.length != 1) + return null; + int categoryId = (Integer)rs[0].get("CategoryId"); + name = (String)rs[0].get("Description"); + + PackageUserSchema packageUserSchema = (PackageUserSchema)_sndSchema.getUserSchema(PackageUserSchema.SCHEMA_NAME); + if (null == packageUserSchema) + return null; + + /* NOTE createCategoryTable() is implemented by PackageUserSchema for implementation sharing. + * I suppose you could argue since we're casting anyway that we could just make the required/shared + * methods public, but this works. + */ + return packageUserSchema.createCategoryTable(this, categoryId, name); + } +} From 2ccb959c73a819216b9bffb35da28da1419061b6 Mon Sep 17 00:00:00 2001 From: Matthew Bellew Date: Thu, 12 Dec 2024 17:36:09 -0800 Subject: [PATCH 08/10] Avoid lookup joins for perf --- resources/schemas/snd.xml | 12 +++++++-- src/org/labkey/snd/SNDUserSchema.java | 25 ++++++++---------- .../snd/query/AbstractSNDTableInfo.java | 26 +++++++++++++++++++ src/org/labkey/snd/query/EventDataTable.java | 6 ++--- src/org/labkey/snd/query/EventNotesTable.java | 6 ++--- .../labkey/snd/query/EventsCacheTable.java | 6 ++--- src/org/labkey/snd/query/EventsTable.java | 12 ++++++--- src/org/labkey/snd/query/PackagesTable.java | 6 ++--- .../labkey/snd/query/SuperPackagesTable.java | 6 ++--- 9 files changed, 71 insertions(+), 34 deletions(-) create mode 100644 src/org/labkey/snd/query/AbstractSNDTableInfo.java diff --git a/resources/schemas/snd.xml b/resources/schemas/snd.xml index 787f5e04..ea90e8ff 100644 --- a/resources/schemas/snd.xml +++ b/resources/schemas/snd.xml @@ -236,8 +236,16 @@ - - + + + NOLOOKUP + + + + + NOLOOKUP + + diff --git a/src/org/labkey/snd/SNDUserSchema.java b/src/org/labkey/snd/SNDUserSchema.java index 7708689e..98e211b5 100644 --- a/src/org/labkey/snd/SNDUserSchema.java +++ b/src/org/labkey/snd/SNDUserSchema.java @@ -31,6 +31,7 @@ import org.labkey.api.query.UserSchema; import org.labkey.api.security.User; import org.labkey.api.security.roles.Role; +import org.labkey.snd.query.AbstractSNDTableInfo; import org.labkey.snd.query.AttributeDataTable; import org.labkey.snd.query.CategoriesTable; import org.labkey.snd.query.EventDataTable; @@ -100,7 +101,7 @@ public enum TableType @Override public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) { - return new SuperPackagesTable(schema, SNDSchema.getInstance().getTableInfoSuperPkgs(), cf).init(); + return new SuperPackagesTable(schema, SNDSchema.getInstance().getTableInfoSuperPkgs()).init(); } }, Pkgs @@ -108,7 +109,7 @@ public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) @Override public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) { - return new PackagesTable(schema, SNDSchema.getInstance().getTableInfoPkgs(), cf).init(); + return new PackagesTable(schema, SNDSchema.getInstance().getTableInfoPkgs()).init(); } }, PkgCategories @@ -125,10 +126,8 @@ public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) @Override public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) { - SimpleUserSchema.SimpleTable table = - new SimpleUserSchema.SimpleTable<>( - schema, SNDSchema.getInstance().getTableInfoPkgCategoryJunction(), cf).init(); - + var table = new AbstractSNDTableInfo(schema, SNDSchema.getInstance().getTableInfoPkgCategoryJunction()) {}; + table.init(); return table; } }, @@ -137,10 +136,8 @@ public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) @Override public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) { - SimpleUserSchema.SimpleTable table = - new SimpleUserSchema.SimpleTable<>( - schema, SNDSchema.getInstance().getTableInfoProjectItems(), cf).init(); - + var table = new AbstractSNDTableInfo(schema, SNDSchema.getInstance().getTableInfoProjectItems()) {}; + table.init(); return table; } }, @@ -159,7 +156,7 @@ public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) { if (schema.getContainer().hasPermission(schema.getUser(), SNDViewerPermission.class, schema.getContextualRoles())) { - return new EventsTable(schema, SNDSchema.getInstance().getTableInfoEvents(), cf).init(); + return new EventsTable(schema, SNDSchema.getInstance().getTableInfoEvents()).init(); } return null; } @@ -171,7 +168,7 @@ public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) { if (schema.getContainer().hasPermission(schema.getUser(), SNDViewerPermission.class, schema.getContextualRoles())) { - return new EventNotesTable(schema, SNDSchema.getInstance().getTableInfoEventNotes(), cf).init(); + return new EventNotesTable(schema, SNDSchema.getInstance().getTableInfoEventNotes()).init(); } return null; @@ -184,7 +181,7 @@ public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) { if (schema.getContainer().hasPermission(schema.getUser(), SNDViewerPermission.class, schema.getContextualRoles())) { - return new EventDataTable(schema, SNDSchema.getInstance().getTableInfoEventData(), cf).init(); + return new EventDataTable(schema, SNDSchema.getInstance().getTableInfoEventData()).init(); } return null; @@ -239,7 +236,7 @@ public TableInfo createTable(SNDUserSchema schema, ContainerFilter cf) { if (schema.getContainer().hasPermission(schema.getUser(), SNDViewerPermission.class, schema.getContextualRoles())) { - return new EventsCacheTable(schema, SNDSchema.getInstance().getTableInfoEventsCache(), cf).init(); + return new EventsCacheTable(schema, SNDSchema.getInstance().getTableInfoEventsCache()).init(); } return null; diff --git a/src/org/labkey/snd/query/AbstractSNDTableInfo.java b/src/org/labkey/snd/query/AbstractSNDTableInfo.java new file mode 100644 index 00000000..707efc9e --- /dev/null +++ b/src/org/labkey/snd/query/AbstractSNDTableInfo.java @@ -0,0 +1,26 @@ +package org.labkey.snd.query; + +import org.labkey.api.data.TableInfo; +import org.labkey.api.query.SimpleUserSchema; +import org.labkey.api.util.ContainerContext; +import org.labkey.snd.SNDUserSchema; + +abstract public class AbstractSNDTableInfo extends SimpleUserSchema.SimpleTable +{ + protected AbstractSNDTableInfo(SNDUserSchema schema, TableInfo dbtable) + { + super(schema, dbtable, null); + } + + @Override + public boolean supportsContainerFilter() + { + return false; + } + + @Override + public ContainerContext getContainerContext() + { + return getUserSchema().getContainer(); + } +} diff --git a/src/org/labkey/snd/query/EventDataTable.java b/src/org/labkey/snd/query/EventDataTable.java index 14fbf96f..f8587c84 100644 --- a/src/org/labkey/snd/query/EventDataTable.java +++ b/src/org/labkey/snd/query/EventDataTable.java @@ -54,7 +54,7 @@ import java.util.Map; import java.util.Set; -public class EventDataTable extends SimpleUserSchema.SimpleTable +public class EventDataTable extends AbstractSNDTableInfo { /** * Create the simple table. @@ -63,9 +63,9 @@ public class EventDataTable extends SimpleUserSchema.SimpleTable * @param schema * @param table */ - public EventDataTable(SNDUserSchema schema, TableInfo table, ContainerFilter cf) + public EventDataTable(SNDUserSchema schema, TableInfo table) { - super(schema, table, cf); + super(schema, table); } public void addColumns() diff --git a/src/org/labkey/snd/query/EventNotesTable.java b/src/org/labkey/snd/query/EventNotesTable.java index a0bd74d3..2995536a 100644 --- a/src/org/labkey/snd/query/EventNotesTable.java +++ b/src/org/labkey/snd/query/EventNotesTable.java @@ -40,7 +40,7 @@ import java.util.List; import java.util.Map; -public class EventNotesTable extends SimpleUserSchema.SimpleTable +public class EventNotesTable extends AbstractSNDTableInfo { /** * Create the simple table. @@ -49,9 +49,9 @@ public class EventNotesTable extends SimpleUserSchema.SimpleTable * @param schema * @param table */ - public EventNotesTable(SNDUserSchema schema, TableInfo table, ContainerFilter cf) + public EventNotesTable(SNDUserSchema schema, TableInfo table) { - super(schema, table, cf); + super(schema, table); } @Override diff --git a/src/org/labkey/snd/query/EventsCacheTable.java b/src/org/labkey/snd/query/EventsCacheTable.java index c0ec01e7..2be1d3cd 100644 --- a/src/org/labkey/snd/query/EventsCacheTable.java +++ b/src/org/labkey/snd/query/EventsCacheTable.java @@ -30,7 +30,7 @@ import java.util.ArrayList; import java.util.List; -public class EventsCacheTable extends SimpleUserSchema.SimpleTable +public class EventsCacheTable extends AbstractSNDTableInfo { /** * Create the simple table. @@ -39,9 +39,9 @@ public class EventsCacheTable extends SimpleUserSchema.SimpleTable defaultVisibleColumns = new ArrayList<>(); diff --git a/src/org/labkey/snd/query/EventsTable.java b/src/org/labkey/snd/query/EventsTable.java index 5b511a67..259da80b 100644 --- a/src/org/labkey/snd/query/EventsTable.java +++ b/src/org/labkey/snd/query/EventsTable.java @@ -53,7 +53,7 @@ import java.util.Map; import java.util.Set; -public class EventsTable extends SimpleTable +public class EventsTable extends AbstractSNDTableInfo { /** * Create the simple table. @@ -64,9 +64,15 @@ public class EventsTable extends SimpleTable */ private final SNDManager _sndManager = SNDManager.get(); - public EventsTable(SNDUserSchema schema, TableInfo table, ContainerFilter cf) + public EventsTable(SNDUserSchema schema, TableInfo table) { - super(schema, table, cf); + super(schema, table); + } + + @Override + public boolean supportsContainerFilter() + { + return super.supportsContainerFilter(); } @Override diff --git a/src/org/labkey/snd/query/PackagesTable.java b/src/org/labkey/snd/query/PackagesTable.java index ae8bf938..fc9686b9 100644 --- a/src/org/labkey/snd/query/PackagesTable.java +++ b/src/org/labkey/snd/query/PackagesTable.java @@ -63,7 +63,7 @@ /** * Created by marty on 8/23/2017. */ -public class PackagesTable extends SimpleTable +public class PackagesTable extends AbstractSNDTableInfo { /** @@ -73,9 +73,9 @@ public class PackagesTable extends SimpleTable * @param schema * @param table */ - public PackagesTable(SNDUserSchema schema, TableInfo table, ContainerFilter cf) + public PackagesTable(SNDUserSchema schema, TableInfo table) { - super(schema, table, cf); + super(schema, table); } @Override diff --git a/src/org/labkey/snd/query/SuperPackagesTable.java b/src/org/labkey/snd/query/SuperPackagesTable.java index ad1ca128..be4fdebc 100644 --- a/src/org/labkey/snd/query/SuperPackagesTable.java +++ b/src/org/labkey/snd/query/SuperPackagesTable.java @@ -50,7 +50,7 @@ /** * Created by marty on 8/23/2017. */ -public class SuperPackagesTable extends SimpleTable +public class SuperPackagesTable extends AbstractSNDTableInfo { /** @@ -60,9 +60,9 @@ public class SuperPackagesTable extends SimpleTable * @param schema * @param table */ - public SuperPackagesTable(SNDUserSchema schema, TableInfo table, ContainerFilter cf) + public SuperPackagesTable(SNDUserSchema schema, TableInfo table) { - super(schema, table, cf); + super(schema, table); } @Override From 6d259afc0ccf831bcf63f3bdee3b715f612ce97a Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Tue, 31 Dec 2024 04:28:33 -0800 Subject: [PATCH 09/10] Bump schema versions to 25.000 (#248) --- src/org/labkey/snd/SNDModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/snd/SNDModule.java b/src/org/labkey/snd/SNDModule.java index 37a5813d..7c1f2851 100644 --- a/src/org/labkey/snd/SNDModule.java +++ b/src/org/labkey/snd/SNDModule.java @@ -67,7 +67,7 @@ public String getName() @Override public @Nullable Double getSchemaVersion() { - return 24.000; + return 25.000; } @Override From 154987b31a208c1f3edd6f8442c76c55fc05b8d9 Mon Sep 17 00:00:00 2001 From: Marty Pradere Date: Mon, 6 Jan 2025 11:39:20 -0800 Subject: [PATCH 10/10] Remove commented out code --- src/org/labkey/snd/query/EventDataTable.java | 111 ------------------- 1 file changed, 111 deletions(-) diff --git a/src/org/labkey/snd/query/EventDataTable.java b/src/org/labkey/snd/query/EventDataTable.java index f8587c84..bacc7c90 100644 --- a/src/org/labkey/snd/query/EventDataTable.java +++ b/src/org/labkey/snd/query/EventDataTable.java @@ -20,7 +20,6 @@ import org.jetbrains.annotations.Nullable; import org.labkey.api.data.BaseColumnInfo; import org.labkey.api.data.Container; -import org.labkey.api.data.ContainerFilter; import org.labkey.api.data.DbSchema; import org.labkey.api.data.DbScope; import org.labkey.api.data.JdbcType; @@ -101,116 +100,6 @@ public boolean hasPermission(@NotNull UserPrincipal user, @NotNull Class attributes = null; - - Map getAttributes() - { - Set ambiguiousShortNames = new CaseInsensitiveHashSet(); - if (null == attributes) - { - // CONSIDER: cache for Packages? - Map packages = new HashMap<>(); - new SqlSelector(SNDSchema.getInstance().getSchema(), - new SQLFragment("SELECT PkgId, Description FROM snd.Pkgs WHERE Container = ").appendValue(getContainer()) - ).fillValueMap(packages); - - Map map = new CaseInsensitiveHashMap<>(); - UserSchema userSchema = getUserSchema(); - Container c = userSchema.getContainer(); - // urn:lsid:labkey.com:package-snd.Folder-13:Package-820 - List list = PropertyService.get().getDomains(userSchema.getContainer(), userSchema.getUser(), false) - .stream().filter(d -> d.getTypeURI().contains(":package-snd.Folder-" + c.getRowId() + ":Package-")) - .toList(); - - for (var d : list) - { - d.getProperties().forEach(dp -> - { - PropertyDescriptor pd = dp.getPropertyDescriptor(); - String uri = dp.getPropertyURI(); - String domainName = d.getName(); - if (domainName.startsWith("Package-")) - { - try - { - var id = Integer.parseInt(domainName.substring("Package-".length())); - var s = packages.get(id); - if (null != s) - domainName = s; - } - catch (NumberFormatException ignore) - { - // pass - } - } - String domainColumn = domainName + "." + dp.getName(); - String column = dp.getName(); - if (null != map.put(uri,pd)) - ambiguiousShortNames.add(uri); - if (null != map.put(domainColumn,pd)) - ambiguiousShortNames.add(domainColumn); - if (null != map.put(column,pd)) - ambiguiousShortNames.add(column); - }); - } - for (String name : ambiguiousShortNames) - map.remove(name); - attributes = Collections.unmodifiableMap(map); - } - return attributes; - } - - MethodInfo attributeMethod = new AbstractTableMethodInfo(JdbcType.OTHER) - { - @Override - public JdbcType getJdbcType(JdbcType[] args) - { - // UNDONE: would be nice to have the actual arguments to inspect - return super.getJdbcType(args); - } - - @Override - public SQLFragment getSQL(String tableAlias, DbSchema schema, SQLFragment[] arguments) - { - ColumnInfo objectId = getColumn("ObjectId"); - if (null == objectId || arguments.length != 1) - { - return new SQLFragment(" NULL "); - } - try - { - String attributeName = QueryService.get().toSimpleString(arguments[0]); - var attributes = getAttributes(); - var pd = attributes.get(attributeName); - if (null == pd) - return new SQLFragment(" 'not found' "); - PropertyColumn pc = new PropertyColumn(pd, objectId, getUserSchema().getContainer(), getUserSchema().getUser(), false); - pc.setParentIsObjectId(true); - return pc.getValueSql(tableAlias); - } - catch (IllegalArgumentException x) - { - throw new QueryParseException("Constant string literal expected for attribute() method", x, -1, -1); - } - } - }; - - @Override - public MethodInfo getMethod(String name) - { - if ("Attribute".equalsIgnoreCase(name)) - return attributeMethod; - return super.getMethod(name); - } - - @Override - public Set getMethodRequiredFieldKeys() - { - return Set.of(new FieldKey(null, "ObjectId")); - } -*/ - @Override public QueryUpdateService getUpdateService() {