Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
aa418d8
Bump sqlalchemy from 1.2.11 to 1.3.0
dependabot[bot] Feb 12, 2023
cff2cb2
Merge branch 'production' into dependabot/pip/sqlalchemy-1.3.0
realVinayak Nov 3, 2023
dcd9889
Begin refactoring deferred scope
realVinayak May 18, 2024
23c6050
get rid of deferred scoping
realVinayak May 18, 2024
1e2e7ff
Begin working on unit tests
realVinayak May 18, 2024
1ce894b
fixup tests
realVinayak May 18, 2024
ee27d51
Add (force?) types
realVinayak May 18, 2024
9264668
Simplify types
realVinayak May 18, 2024
03efb8e
Make backend mostly compatible with handling generic fields + relatio…
realVinayak May 21, 2024
8e404a6
don't set field to str if no tree match
realVinayak May 21, 2024
bccb657
Add type + fix id field not misunderstood as formatted
realVinayak May 21, 2024
7d4157d
Merge branch 'production' into wb_improvements
realVinayak May 22, 2024
eab69a5
Merge remote-tracking branch 'origin/production' into wb_improvements
realVinayak May 22, 2024
6821a88
Add experimental support for arbitrary nested-to-manys in workbench
realVinayak Jun 9, 2024
6ceedbb
Fix remaining unit test
realVinayak Jun 9, 2024
19a8dcf
Begin adding unit tests
realVinayak Jun 9, 2024
f2b2e46
fix mypy errors
realVinayak Jun 9, 2024
aacd880
Add matching test
realVinayak Jun 9, 2024
b5300dc
Merge remote-tracking branch 'origin/production' into wb_improvements
realVinayak Jun 19, 2024
3f1d33a
Merge branch 'production' into dependabot/pip/sqlalchemy-1.3.0
realVinayak Jun 19, 2024
3e17437
Merge remote-tracking branch 'origin/production' into wb_improvements
realVinayak Jul 8, 2024
0663155
Handle multiple matches with one-to-ones more smartly
realVinayak Jul 8, 2024
2f32a9b
Remove the need of passing collection to bind
realVinayak Jul 8, 2024
41c65e3
Test improvements
realVinayak Jul 8, 2024
091a53b
Lint code with ESLint and Prettier
realVinayak Jul 8, 2024
9cd84a0
Pass name explictly
realVinayak Jul 8, 2024
3ee24d0
Merge branch 'wb_improvements' of https://github.com/specify/specify7…
realVinayak Jul 8, 2024
8bcb3a3
Merge pull request #785 from specify/dependabot/pip/sqlalchemy-1.3.0
realVinayak Jul 29, 2024
6a6a234
(feature): Allow nested-to-manys on front-end
realVinayak Aug 2, 2024
7b5b583
(bug): Resolve to-many being skipped
realVinayak Aug 2, 2024
22a75f5
(bug): Resolve fake multiple matches due to dup
realVinayak Aug 6, 2024
b59a894
(feat): Remove redundant to-many for backwards compability
realVinayak Aug 6, 2024
d8b1d49
(feat): Minor batch-edit work
realVinayak Jun 1, 2024
39117b1
(feat): Batch-edit
realVinayak Aug 16, 2024
4ad5b11
(bug): mypy + .env
realVinayak Aug 16, 2024
755eba2
(bug): mypy resolve
realVinayak Aug 16, 2024
1e8e90b
(batch-edit): unit tests + bug resolves
realVinayak Aug 19, 2024
9f16726
(tests): Backend unit tests
realVinayak Aug 20, 2024
b36aa17
(batch-edit): front-end resolves
realVinayak Aug 20, 2024
4de007a
(tests): localization resolves
realVinayak Aug 20, 2024
2fd6e6e
(tests): Resolve tests + clear results on rollback
realVinayak Aug 20, 2024
8b2433f
(batch-edit): refactors, and unit tests
realVinayak Aug 22, 2024
f3913c5
(batch-edit): UI changes
realVinayak Aug 22, 2024
c09ad24
(test): localization test resolve
realVinayak Aug 22, 2024
aca7b66
Lint code with ESLint and Prettier
realVinayak Aug 22, 2024
c0f26b6
(build): Add back changed build file
realVinayak Aug 22, 2024
0467c39
Merge branch 'wb_improvements' of https://github.com/specify/specify7…
realVinayak Aug 22, 2024
3dcb5ce
(batch-edit): make tree query more robust
realVinayak Aug 23, 2024
afc6362
Lint code with ESLint and Prettier
realVinayak Aug 23, 2024
28b823b
(batch-edit): enforce name + resolve wrong order
realVinayak Aug 23, 2024
6a0b711
(batch-edit): minor refactor
realVinayak Aug 24, 2024
7244e44
(batch-edit): make deletes more sensible, simplify naiveness
realVinayak Aug 28, 2024
15e0d14
(batch-edit): revert workbench views
realVinayak Aug 28, 2024
0c4dac0
(batch-edit): Restrict system tables
realVinayak Aug 28, 2024
1191148
(batch-edit): UI comment resolves
realVinayak Aug 29, 2024
462dd4a
(batch-edit): rollback improvements wip
realVinayak Aug 29, 2024
bc50824
(batch-edit): type resolve
realVinayak Aug 29, 2024
70bc794
Lint code with ESLint and Prettier
realVinayak Aug 29, 2024
20fcc55
(batch-edit): make float casts smarter
realVinayak Aug 29, 2024
0cdb7be
Merge branch 'wb_improvements' of https://github.com/specify/specify7…
realVinayak Aug 29, 2024
6bb84c0
(batch-edit): Fix column id being off by 1. thanks theresa
realVinayak Aug 29, 2024
79600c3
(batch-edit): PR resolves
realVinayak Aug 30, 2024
57292ca
Lint code with ESLint and Prettier
realVinayak Aug 30, 2024
60bb322
(tests): type resolve
realVinayak Aug 30, 2024
c07c867
Merge branch 'wb_improvements' of https://github.com/specify/specify7…
realVinayak Aug 30, 2024
9bc64f1
(tests): type resolve values
realVinayak Aug 30, 2024
aa11b35
(migration): Resolve isupdate correctly
realVinayak Aug 30, 2024
3bed2f5
(batch-edit): add permission + make hier RO
realVinayak Sep 16, 2024
ab89d28
(batch-edit): add doc urls
realVinayak Sep 16, 2024
f050869
(batch-edit): move pref to upload plan
realVinayak Sep 18, 2024
d9b19a7
(batch-edit): make upload plan adjustments
realVinayak Sep 23, 2024
5efecac
(batch-edit): adjust tests
realVinayak Sep 23, 2024
2d86ba4
Lint code with ESLint and Prettier
realVinayak Sep 23, 2024
62f0762
Merge remote-tracking branch 'origin/production' into wb_improvements
sharadsw Dec 18, 2024
2cfcc18
Merge remote-tracking branch 'origin/production' into wb_improvements
sharadsw Dec 18, 2024
77b3133
Merge remote-tracking branch 'origin/production' into wb_improvements
sharadsw Dec 18, 2024
95111dd
query field type fixes
sharadsw Dec 18, 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
3 changes: 2 additions & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

DATABASE_HOST=mariadb
DATABASE_PORT=3306
MYSQL_ROOT_PASSWORD=password
Expand Down Expand Up @@ -39,4 +40,4 @@ LOG_LEVEL=WARNING
# should only be used during development and troubleshooting and not
# during general use. Django applications leak memory when operated
# continuously in debug mode.
SP7_DEBUG=true
SP7_DEBUG=true
5 changes: 0 additions & 5 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ jobs:
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
echo "created=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT

- name: Docker meta
id: meta
uses: docker/metadata-action@v5
Expand All @@ -76,7 +75,6 @@ jobs:
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
Expand All @@ -93,7 +91,6 @@ jobs:
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"

- name: Upload digest
uses: actions/upload-artifact@v4
with:
Expand Down Expand Up @@ -122,13 +119,11 @@ jobs:
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY_IMAGE }}

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Create manifest list and push
working-directory: /tmp/digests
run: |
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ PyJWT==2.3.0
django-auth-ldap==1.2.15
jsonschema==3.2.0
typing-extensions==4.3.0
django-model-utils==4.4.0
1 change: 0 additions & 1 deletion specifyweb/businessrules/uniqueness_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@

logger = logging.getLogger(__name__)


@orm_signal_handler('pre_save', None, dispatch_uid=UNIQUENESS_DISPATCH_UID)
def check_unique(model, instance):
model_name = instance.__class__.__name__
Expand Down
2 changes: 1 addition & 1 deletion specifyweb/businessrules/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,4 +294,4 @@ def validate_uniqueness(request):
"fields": [{"duplicates": duplicate[duplicates_field], "fields": {field: value for field, value in duplicate.items() if field != duplicates_field}}
for duplicate in duplicates]}

return http.JsonResponse(final, safe=False)
return http.JsonResponse(final, safe=False)
4 changes: 2 additions & 2 deletions specifyweb/export/dwca.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
from xml.etree import ElementTree as ET
from xml.dom import minidom

from specifyweb.stored_queries.execution import EphemeralField, query_to_csv
from specifyweb.stored_queries.queryfield import QueryField
from specifyweb.stored_queries.execution import query_to_csv
from specifyweb.stored_queries.queryfield import QueryField, EphemeralField
from specifyweb.stored_queries.models import session_context

logger = logging.getLogger(__name__)
Expand Down
147 changes: 93 additions & 54 deletions specifyweb/express_search/related.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,67 +5,79 @@
import logging

from ..specify.models import datamodel
from ..stored_queries.execution import build_query
from ..stored_queries.execution import BuildQueryProps, build_query
from ..stored_queries.query_ops import QueryOps
from ..stored_queries.queryfield import QueryField
from ..stored_queries.queryfieldspec import QueryFieldSpec

logger = logging.getLogger(__name__)


class F(str):
pass


class RelatedSearchMeta(type):
def __new__(cls, name, bases, dict):
Rs = super(RelatedSearchMeta, cls).__new__(cls, name, bases, dict)
if Rs.definitions is None:
return Rs

root_table_name = Rs.definitions[0].split('.')[0]
root_table_name = Rs.definitions[0].split(".")[0]

Rs.root = datamodel.get_table(root_table_name, strict=True)

def col_to_fs(col, add_id=False):
return QueryFieldSpec.from_path( [root_table_name] + col.split('.'), add_id )
return QueryFieldSpec.from_path([root_table_name] + col.split("."), add_id)

Rs.display_fields = [
QueryField(fieldspec=col_to_fs(col),
op_num=1,
value="",
negate=False,
display=True,
format_name=None,
sort_type=0,
strict=False)
for col in Rs.columns]

if Rs.link:
Rs.display_fields.append(QueryField(
fieldspec=col_to_fs(Rs.link, add_id=True),
QueryField(
fieldspec=col_to_fs(col),
op_num=1,
value="",
negate=False,
display=True,
format_name=None,
sort_type=0,
strict=False))
strict=False
)
for col in Rs.columns
]

if Rs.link:
Rs.display_fields.append(
QueryField(
fieldspec=col_to_fs(Rs.link, add_id=True),
op_num=1,
value="",
negate=False,
display=True,
format_name=None,
sort_type=0,
strict=False
)
)

def make_filter(f, negate):
field, op, val = f
return QueryField(fieldspec=col_to_fs(field),
op_num=QueryOps.OPERATIONS.index(op.__name__),
value=col_to_fs(val) if isinstance(val, F) else val,
negate=negate,
display=False,
format_name=None,
sort_type=0,
strict=False)

Rs.filter_fields = [make_filter(f, False) for f in Rs.filters] + \
[make_filter(f, True) for f in Rs.excludes]
return QueryField(
fieldspec=col_to_fs(field),
op_num=QueryOps.OPERATIONS.index(op.__name__),
value=col_to_fs(val) if isinstance(val, F) else val,
negate=negate,
display=False,
format_name=None,
sort_type=0,
strict=False
)

Rs.filter_fields = [make_filter(f, False) for f in Rs.filters] + [
make_filter(f, True) for f in Rs.excludes
]

return Rs


class RelatedSearch(object, metaclass=RelatedSearchMeta):
distinct = False
filters = []
Expand All @@ -76,9 +88,14 @@ class RelatedSearch(object, metaclass=RelatedSearchMeta):

@classmethod
def execute(cls, session, config, terms, collection, user, limit, offset):
queries = [_f for _f in (
cls(defn).build_related_query(session, config, terms, collection, user)
for defn in cls.definitions) if _f]
queries = [
_f
for _f in (
cls(defn).build_related_query(session, config, terms, collection, user)
for defn in cls.definitions
)
if _f
]

if len(queries) > 0:
query = queries[0].union(*queries[1:])
Expand All @@ -89,64 +106,86 @@ def execute(cls, session, config, terms, collection, user, limit, offset):
results = []

return {
'totalCount': count,
'results': results,
'definition': {
'name': cls.__name__,
'root': cls.root.name,
'link': cls.link,
'columns': cls.columns,
'fieldSpecs': [{'stringId': fs.to_stringid(), 'isRelationship': fs.is_relationship()}
for field in cls.display_fields
for fs in [field.fieldspec]]}}
"totalCount": count,
"results": results,
"definition": {
"name": cls.__name__,
"root": cls.root.name,
"link": cls.link,
"columns": cls.columns,
"fieldSpecs": [
{
"stringId": fs.to_stringid(),
"isRelationship": fs.is_relationship(),
}
for field in cls.display_fields
for fs in [field.fieldspec]
],
},
}

def __init__(self, definition):
self.definition = definition

def build_related_query(self, session, config, terms, collection, user):
logger.info('%s: building related query using definition: %s',
self.__class__.__name__, self.definition)
logger.info(
"%s: building related query using definition: %s",
self.__class__.__name__,
self.definition,
)

from .views import build_primary_query

primary_fieldspec = QueryFieldSpec.from_path(self.definition.split('.'), add_id=True)
primary_fieldspec = QueryFieldSpec.from_path(
self.definition.split("."), add_id=True
)

pivot = primary_fieldspec.table

logger.debug('pivoting on: %s', pivot)
for searchtable in config.findall('tables/searchtable'):
if searchtable.find('tableName').text == pivot.name:
logger.debug("pivoting on: %s", pivot)
for searchtable in config.findall("tables/searchtable"):
if searchtable.find("tableName").text == pivot.name:
break
else:
return None

logger.debug('using %s for primary search', searchtable.find('tableName').text)
primary_query = build_primary_query(session, searchtable, terms, collection, user, as_scalar=True)
logger.debug("using %s for primary search", searchtable.find("tableName").text)
primary_query = build_primary_query(
session, searchtable, terms, collection, user, as_scalar=True
)

if primary_query is None:
return None
logger.debug('primary query: %s', primary_query)
logger.debug("primary query: %s", primary_query)

primary_field = QueryField(
fieldspec=primary_fieldspec,
op_num=QueryOps.OPERATIONS.index('op_in'),
op_num=QueryOps.OPERATIONS.index("op_in"),
value=primary_query,
negate=False,
display=False,
format_name=None,
sort_type=0,
strict=False)
strict=False
)

logger.debug("primary queryfield: %s", primary_field)
logger.debug("display queryfields: %s", self.display_fields)
logger.debug("filter queryfields: %s", self.filter_fields)

queryfields = self.display_fields + self.filter_fields + [primary_field]

related_query, _ = build_query(session, collection, user, self.root.tableId, queryfields, implicit_or=False)
related_query, _ = build_query(
session,
collection,
user,
self.root.tableId,
queryfields,
props=BuildQueryProps(implicit_or=True),
)

if self.distinct:
related_query = related_query.distinct()

logger.debug('related query: %s', related_query)
logger.debug("related query: %s", related_query)
return related_query
10 changes: 8 additions & 2 deletions specifyweb/frontend/js_src/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,16 @@
--invalid-cell: theme('colors.red.300');
--modified-cell: theme('colors.yellow.250');
--search-result: theme('colors.green.300');
@apply dark:[--invalid-cell:theme('colors.red.900')]
--updated-cell: theme('colors.cyan.200');
--deleted-cell: theme('colors.amber.500');
--matched-and-changed-cell: theme('colors.blue.200');
@apply dark:[--deleted-cell:theme('colors.amber.600')]
dark:[--invalid-cell:theme('colors.red.900')]
dark:[--matched-and-changed-cell:theme('colors.fuchsia.700')]
dark:[--modified-cell:theme('colors.yellow.900')]
dark:[--new-cell:theme('colors.indigo.900')]
dark:[--search-result:theme('colors.green.900')];
dark:[--search-result:theme('colors.green.900')]
dark:[--updated-cell:theme('colors.cyan.800')];
}

.custom-select {
Expand Down
27 changes: 20 additions & 7 deletions specifyweb/frontend/js_src/css/workbench.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@
}

/* CONTENT styles */
.wbs-form.wb-show-upload-results .wb-no-match-cell,
.wbs-form.wb-show-upload-results .wb-no-match-cell
.wbs-form.wb-show-upload-results .wb-updated-cell
.wbs-form.wb-show-upload-results .wb-deleted-cell
.wbs-form.wb-show-upload-results .wb-matched-and-changed-cell
.wbs-form.wb-focus-coordinates .wb-coordinate-cell {
@apply text-black dark:text-white;
}
Expand All @@ -50,18 +53,28 @@
/* Cell navigation */

.wbs-form
:is(
.wb-no-match-cell,
.wb-modified-cell,
.htCommentCell,
.wb-search-match-cell
),
:is(.wb-no-match-cell, .wb-modified-cell, .htCommentCell, .wb-search-match-cell, .wb-updated-cell, .wb-deleted-cell, .wb-matched-and-changed-cell),
.wb-navigation-section {
@apply !bg-[color:var(--accent-color)];
}

/* The order here determines the priority of the states
* From the lowest till the highest */
.wbs-form:not(.wb-hide-new-cells) .wb-updated-cell,
.wb-navigation-section[data-navigation-type='updatedCells'] {
--accent-color: var(--updated-cell);
}

.wbs-form:not(.wb-hide-new-cells) .wb-deleted-cell,
.wb-navigation-section[data-navigation-type='deletedCells'] {
--accent-color: var(--deleted-cell);
}

.wbs-form:not(.wb-hide-new-cells) .wb-matched-and-changed-cell,
.wb-navigation-section[data-navigation-type='matchedAndChangedCells'] {
--accent-color: var(--matched-and-changed-cell);
}

.wbs-form:not(.wb-hide-new-cells) .wb-no-match-cell,
.wb-navigation-section[data-navigation-type='newCells'] {
--accent-color: var(--new-cell);
Expand Down
Loading