Skip to content
Open
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
1 change: 1 addition & 0 deletions api/src/org/labkey/api/exp/query/ExpDataTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ enum Column
SourceProtocolApplication,
SourceApplicationInput,
DataFileUrl,
ReferenceCount,
Run,
RunApplication,
RunApplicationOutput,
Expand Down
16 changes: 16 additions & 0 deletions api/src/org/labkey/api/exp/query/ExpSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.labkey.api.security.User;
import org.labkey.api.security.permissions.InsertPermission;
import org.labkey.api.security.permissions.ReadPermission;
import org.labkey.api.settings.AppProps;
import org.labkey.api.util.StringExpression;
import org.labkey.api.view.ActionURL;
import org.labkey.api.view.ViewContext;
Expand All @@ -70,6 +71,7 @@ public class ExpSchema extends AbstractExpSchema
public static final String SAMPLE_STATE_TYPE_TABLE = "SampleStateType";
public static final String SAMPLE_TYPE_CATEGORY_TABLE = "SampleTypeCategoryType";
public static final String MEASUREMENT_UNITS_TABLE = "MeasurementUnits";
public static final String SAMPLE_FILES_TABLE = "UnreferencedSampleFiles";

public static final SchemaKey SCHEMA_EXP = SchemaKey.fromParts(ExpSchema.SCHEMA_NAME);
public static final SchemaKey SCHEMA_EXP_DATA = SchemaKey.fromString(SCHEMA_EXP, ExpSchema.NestedSchemas.data.name());
Expand Down Expand Up @@ -220,6 +222,20 @@ public TableInfo createTable(ExpSchema expSchema, String queryName, ContainerFil
return expSchema.setupTable(result);
}
},
UnreferencedSampleFiles
{
@Override
public TableInfo createTable(ExpSchema expSchema, String queryName, ContainerFilter cf)
{
return new ExpUnreferencedSampleFilesTable(expSchema, cf);
}

@Override
public boolean includeTable()
{
return AppProps.getInstance().isOptionalFeatureEnabled(SAMPLE_FILES_TABLE);
}
},
SampleStatus
{
@Override
Expand Down
113 changes: 113 additions & 0 deletions api/src/org/labkey/api/exp/query/ExpUnreferencedSampleFilesTable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package org.labkey.api.exp.query;

import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.labkey.api.data.BaseColumnInfo;
import org.labkey.api.data.ContainerFilter;
import org.labkey.api.data.CoreSchema;
import org.labkey.api.data.JdbcType;
import org.labkey.api.data.SQLFragment;
import org.labkey.api.data.TableInfo;
import org.labkey.api.data.VirtualTable;
import org.labkey.api.exp.api.ExperimentService;
import org.labkey.api.files.FileContentService;
import org.labkey.api.query.FilteredTable;
import org.labkey.api.query.UserIdQueryForeignKey;
import org.labkey.api.query.UserSchema;
import org.labkey.api.query.column.BuiltInColumnTypes;

public class ExpUnreferencedSampleFilesTable extends FilteredTable<ExpSchema>
{
public ExpUnreferencedSampleFilesTable(@NotNull ExpSchema schema, ContainerFilter cf)
{
super(createVirtualTable(schema), schema, cf);
setDescription("Contains all sample files that are not referenced by any domain fields.");
wrapAllColumns(true);
}

private static TableInfo createVirtualTable(@NotNull ExpSchema schema)
{
return new ExpUnreferencedSampleFilesTable.FileUnionTable(schema);
}

private static class FileUnionTable extends VirtualTable
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps extends VirtualTable<ExpSchema> and then public FileUnionTable(@NotNull ExpSchema schema)?

{
private final SQLFragment _query;

public FileUnionTable(@NotNull UserSchema schema)
{
super(CoreSchema.getInstance().getSchema(), ExpSchema.SAMPLE_FILES_TABLE, schema);

FileContentService svc = FileContentService.get();

_query = new SQLFragment();
if (svc == null)
return;
_query.appendComment("<SampleFileListTableInfo>", getSchema().getSqlDialect());

TableInfo expDataTable = ExperimentService.get().getTinfoData();
TableInfo materialTable = ExperimentService.get().getTinfoMaterial();

SQLFragment listQuery = svc.listSampleFilesQuery(schema.getUser());
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider moving this check up to avoid writing an open comment to the query.

super(CoreSchema.getInstance().getSchema(), ExpSchema.SAMPLE_FILES_TABLE, schema);

_query = new SQLFragment();
FileContentService svc = FileContentService.get();

if (svc == null)
    return;

SQLFragment listQuery = svc.listSampleFilesQuery(schema.getUser());
if (StringUtils.isEmpty(listQuery))
    return;

_query.appendComment("<SampleFileListTableInfo>", getSchema().getSqlDialect());

// ...

if (StringUtils.isEmpty(listQuery))
return;

SQLFragment sampleFileSql = new SQLFragment("SELECT m.Container, if.FilePathShort \n")
.append("FROM (")
.append(svc.listSampleFilesQuery(schema.getUser()))
.append(") AS if \n")
.append("JOIN ")
.append(materialTable, "m")
.append(" ON if.SourceKey = m.RowId");

SQLFragment unreferencedFileSql = new SQLFragment("SELECT ed.rowId, ed.name as filename, ed.container, ed.created, ed.createdBy, ed.DataFileUrl FROM ")
.append(expDataTable, "ed")
.append(" LEFT JOIN (")
.append(sampleFileSql)
.append(" ) sf\n")
.append(" ON ed.name = sf.FilePathShort AND ed.container = sf.container\n")
.append(" WHERE ed.datafileurl LIKE ")
.appendValue("%@files/sampletype/%")
.append(" AND sf.FilePathShort IS NULL");

_query.append(unreferencedFileSql);

_query.appendComment("</SampleFileListTableInfo>", getSchema().getSqlDialect());

var rowIdCol = new BaseColumnInfo("RowId", this, JdbcType.INTEGER);
rowIdCol.setHidden(true);
rowIdCol.setKeyField(true);
addColumn(rowIdCol);

var fileNameCol = new BaseColumnInfo("FileName", this, JdbcType.VARCHAR);
addColumn(fileNameCol);

if (schema.getUser().hasApplicationAdminPermission())
{
var filePathCol = new BaseColumnInfo("DataFileUrl", this, JdbcType.VARCHAR);
filePathCol.setHidden(true);
addColumn(filePathCol);
}

var containerCol = new BaseColumnInfo("Container", this, JdbcType.VARCHAR);
containerCol.setConceptURI(BuiltInColumnTypes.CONTAINERID_CONCEPT_URI);
addColumn(containerCol);

var createdCol = new BaseColumnInfo("Created", this, JdbcType.DATE);
addColumn(createdCol);

var createdByCol = new BaseColumnInfo("CreatedBy", this, JdbcType.INTEGER);
createdByCol.setFk(new UserIdQueryForeignKey(getUserSchema(), true));
addColumn(createdByCol);
}

@NotNull
@Override
public SQLFragment getFromSQL()
{
return _query;
}

}

}
2 changes: 2 additions & 0 deletions api/src/org/labkey/api/files/FileContentService.java
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,8 @@ default void fireFileDeletedEvent(@NotNull Path deleted, @Nullable User user, @N
*/
SQLFragment listFilesQuery(@NotNull User currentUser);

SQLFragment listSampleFilesQuery(@NotNull User currentUser);

void setWebfilesEnabled(boolean enabled, User user);

/**
Expand Down
5 changes: 5 additions & 0 deletions api/src/org/labkey/api/files/FileListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,9 @@ default void fileDeleted(@NotNull Path deleted, @Nullable User user, @Nullable C
* </ul>
*/
SQLFragment listFilesQuery();

default SQLFragment listSampleFilesQuery()
Copy link
Contributor

Choose a reason for hiding this comment

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

Annotate with @Nullable

{
return null;
}
}
16 changes: 14 additions & 2 deletions api/src/org/labkey/api/files/TableUpdaterFileListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -353,10 +353,10 @@ public Collection<File> listFiles(@Nullable Container container)
@Override
public SQLFragment listFilesQuery()
{
return listFilesQuery(false, null);
return listFilesQuery(false, null, false);
}

public SQLFragment listFilesQuery(boolean skipCreatedModified, String filePath)
public SQLFragment listFilesQuery(boolean skipCreatedModified, CharSequence filePath, boolean extractName)
{
SQLFragment selectFrag = new SQLFragment();
selectFrag.append("SELECT\n");
Expand Down Expand Up @@ -395,6 +395,16 @@ else if (_table.getColumn("Folder") != null)

selectFrag.append(" ").appendIdentifier(_pathColumn.getSelectIdentifier()).append(" AS FilePath,\n");

if (extractName)
{
SqlDialect dialect = _table.getSchema().getSqlDialect();
SQLFragment fileNameFrag = new SQLFragment();
fileNameFrag.append("regexp_replace(").appendIdentifier(_pathColumn.getSelectIdentifier()).append(", ");
fileNameFrag.append(dialect.getStringHandler().quoteStringLiteral(".*/")).append(", ");
fileNameFrag.append(dialect.getStringHandler().quoteStringLiteral("")).append(")");;
selectFrag.append(" ").append(fileNameFrag).append(" AS FilePathShort,\n");
}

if (_keyColumn != null)
selectFrag.append(" ").appendIdentifier(_keyColumn.getSelectIdentifier()).append(" AS SourceKey,\n");
else
Expand All @@ -408,6 +418,8 @@ else if (_table.getColumn("Folder") != null)

if (StringUtils.isEmpty(filePath))
selectFrag.append(" IS NOT NULL\n");
else if (filePath instanceof SQLFragment)
selectFrag.append(" = ").append(filePath).append("\n");
else
selectFrag.append(" = ").appendStringLiteral(filePath, _table.getSchema().getSqlDialect()).append("\n");

Expand Down
14 changes: 14 additions & 0 deletions experiment/src/org/labkey/experiment/ExperimentModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.apache.commons.lang3.math.NumberUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.json.JSONObject;
import org.labkey.api.admin.FolderSerializationRegistry;
import org.labkey.api.assay.AssayProvider;
import org.labkey.api.assay.AssayService;
Expand Down Expand Up @@ -112,6 +113,7 @@
import org.labkey.api.vocabulary.security.DesignVocabularyPermission;
import org.labkey.api.webdav.WebdavResource;
import org.labkey.api.webdav.WebdavService;
import org.labkey.api.writer.ContainerUser;
import org.labkey.experiment.api.DataClassDomainKind;
import org.labkey.experiment.api.ExpDataClassImpl;
import org.labkey.experiment.api.ExpDataClassTableImpl;
Expand Down Expand Up @@ -181,6 +183,7 @@
import static org.labkey.api.data.ColumnRenderPropertiesImpl.STORAGE_UNIQUE_ID_CONCEPT_URI;
import static org.labkey.api.data.ColumnRenderPropertiesImpl.TEXT_CHOICE_CONCEPT_URI;
import static org.labkey.api.exp.api.ExperimentService.MODULE_NAME;
import static org.labkey.api.exp.query.ExpSchema.SAMPLE_FILES_TABLE;

public class ExperimentModule extends SpringModule
{
Expand Down Expand Up @@ -266,6 +269,9 @@ protected void init()
}
else
{
OptionalFeatureService.get().addExperimentalFeatureFlag(SAMPLE_FILES_TABLE, "Manage Unreferenced Sample Files",
"Enable 'Unreferenced Sample Files' table to view and delete sample files that are no longer referenced by samples", false);

OptionalFeatureService.get().addExperimentalFeatureFlag(NameGenerator.EXPERIMENTAL_ALLOW_GAP_COUNTER, "Allow gap with withCounter and rootSampleCount expression",
"Check this option if gaps in the count generated by withCounter or rootSampleCount name expression are allowed.", true);
}
Expand Down Expand Up @@ -1117,4 +1123,12 @@ public Collection<String> getProvisionedSchemaNames()
{
return PageFlowUtil.set(DataClassDomainKind.PROVISIONED_SCHEMA_NAME, SampleTypeDomainKind.PROVISIONED_SCHEMA_NAME);
}

@Override
public JSONObject getPageContextJson(ContainerUser context)
{
JSONObject json = new JSONObject(getDefaultPageContextJson(context.getContainer()));
Copy link
Contributor

Choose a reason for hiding this comment

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

Call super instead

@Override
public JSONObject getPageContextJson(ContainerUser context)
{
    JSONObject json = super.getPageContextJson(context);
    json.put(SAMPLE_FILES_TABLE, OptionalFeatureService.get().isFeatureEnabled(SAMPLE_FILES_TABLE));
    return json;
}

json.put(SAMPLE_FILES_TABLE, OptionalFeatureService.get().isFeatureEnabled(SAMPLE_FILES_TABLE));
return json;
}
}
26 changes: 24 additions & 2 deletions experiment/src/org/labkey/experiment/FileLinkFileListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ public SQLFragment listFilesQuery(boolean skipCreatedModified)
return listFilesQuery(skipCreatedModified, null);
}

public SQLFragment listFilesQuery(boolean skipCreatedModified, String filePath)
public SQLFragment listFilesQuery(boolean skipCreatedModified, CharSequence filePath)
{
final SQLFragment frag = new SQLFragment();

Expand All @@ -298,8 +298,11 @@ public SQLFragment listFilesQuery(boolean skipCreatedModified, String filePath)
frag.append("WHERE\n");
if (StringUtils.isEmpty(filePath))
frag.append(" op.StringValue IS NOT NULL AND\n");
else if (filePath instanceof SQLFragment)
frag.append(" op.StringValue = ").append(filePath).append(" AND\n");
else
frag.append(" op.StringValue = ").appendStringLiteral(filePath, OntologyManager.getTinfoObject().getSqlDialect()).append(" AND\n");

frag.append(" o.ObjectId = op.ObjectId AND\n");
frag.append(" PropertyId IN (\n");
frag.append(" SELECT PropertyId\n");
Expand All @@ -311,7 +314,26 @@ public SQLFragment listFilesQuery(boolean skipCreatedModified, String filePath)
SQLFragment containerFrag = new SQLFragment("?", containerId);
TableUpdaterFileListener updater = new TableUpdaterFileListener(table, pathColumn.getColumnName(), TableUpdaterFileListener.Type.filePath, null, containerFrag);
frag.append("UNION").append(StringUtils.isEmpty(filePath) ? "" : " ALL" /*keep duplicate*/).append("\n");
frag.append(updater.listFilesQuery(skipCreatedModified, filePath));
frag.append(updater.listFilesQuery(skipCreatedModified, filePath, false));
});

return frag;
}

@Override
public SQLFragment listSampleFilesQuery()
{
final SQLFragment frag = new SQLFragment();
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this return null instead of an empty SQLFragment (when it is empty) given the check in FileContentServiceImpl.listSampleFilesQuery() is checking for null to skip including blank UNION statements?

Or switch the check to be if (subselect != null && !subselect.isEmpty()).


hardTableFileLinkColumns((schema, table, pathColumn, containerId, domainUri) -> {
if (schema.getName().equals("expsampleset"))
Copy link
Contributor

Choose a reason for hiding this comment

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

Use case-insensitive check against SampleTypeDomainKind.PROVISIONED_SCHEMA_NAME instead:

hardTableFileLinkColumns((schema, table, pathColumn, containerId, domainUri) -> {
    if (PROVISIONED_SCHEMA_NAME.equalsIgnoreCase(schema.getName()))
    {
        // ...
    }
});

{
SQLFragment containerFrag = new SQLFragment("?", containerId);
TableUpdaterFileListener updater = new TableUpdaterFileListener(table, pathColumn.getColumnName(), TableUpdaterFileListener.Type.filePath, "rowid", containerFrag);
if (!frag.isEmpty())
frag.append("UNION").append("").append("\n");
frag.append(updater.listFilesQuery(true, null, true));
}
});

return frag;
Expand Down
Loading