Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
LocoDelAssembly committed Sep 27, 2024
2 parents 01ee83d + 23690be commit 66c50fc
Show file tree
Hide file tree
Showing 65 changed files with 628 additions and 413 deletions.
27 changes: 23 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,25 @@ This project <em>does not yet</em> adheres to [Semantic Versioning](https://semv

## [unreleased]

\-
## [0.44.2] - 2024-09-27

### Added

- Sort by identifier match option [#4065]
- `/collection_objects/123/dwc_compact` - DwC fields for those populated [#3994]
- Pagination to `/api/v1/otus/:otu_id/inventory/dwc_gallery`

### Fixed

- Display of missing DwC fields [#4051]
- `verbatim_field_number` updates ignored [#4066]
- DwC importer `verbatim_field_number` collision with Identifier validation
- Shortcuts: Keys pressed are not removed after user switches windows/tab

[#4065]: https://github.com/SpeciesFileGroup/taxonworks/issues/4065
[#4051]: https://github.com/SpeciesFileGroup/taxonworks/issues/4051
[#4066]: https://github.com/SpeciesFileGroup/taxonworks/issues/4066
[#3994]: https://github.com/SpeciesFileGroup/taxonworks/issues/3994

## [0.44.1] - 2024-09-24

Expand All @@ -20,7 +38,7 @@ This project <em>does not yet</em> adheres to [Semantic Versioning](https://semv
### Changed

- Revert strict `verbatim_field_number` validation [#4061]
- Renamed CollectingEvent `verbatim_trip_code` to `verbatim_field_number` [#4058]
- Renamed CollectingEvent `verbatim_trip_identifier` to `verbatim_field_number` [#4058]

### Fixed

Expand Down Expand Up @@ -77,7 +95,7 @@ This project <em>does not yet</em> adheres to [Semantic Versioning](https://semv
### Changed

- DwC export will now use a valid taxon name if linked first to an invalid, and it is available
- EventID and verbatim_trip_code are disentangled in DwC Importer, they do not map to one-another now [#3800] [#2852]
- EventID and verbatim_trip_identifier are disentangled in DwC Importer, they do not map to one-another now [#3800] [#2852]
- TripCode is now FieldNumber (all data migrated)
- DwcOccurrence rebuilds triggered for changes to TaxonNameRelationship [#4019], TypeMaterial, TaxonDetermination, Identifier::Global
- Wikidata IDs are now also loaded into recordedByID and identifiedByID [#3989]
Expand Down Expand Up @@ -4859,7 +4877,8 @@ _Special thanks to Tom Klein for his amazing open-source contributions on this r
- Loosing input page numbers when switching tabs on New Taxon Name task

[#1532]: https://github.com/SpeciesFileGroup/taxonworks/issues/1532
[unreleased]: https://github.com/SpeciesFileGroup/taxonworks/compare/v0.44.1..development
[unreleased]: https://github.com/SpeciesFileGroup/taxonworks/compare/v0.44.2..development
[0.44.2]: https://github.com/SpeciesFileGroup/taxonworks/compare/v0.44.1...v0.44.2
[0.44.1]: https://github.com/SpeciesFileGroup/taxonworks/compare/v0.44.0...v0.44.1
[0.44.0]: https://github.com/SpeciesFileGroup/taxonworks/compare/v0.43.3...v0.44.0
[0.43.3]: https://github.com/SpeciesFileGroup/taxonworks/compare/v0.43.2...v0.43.3
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ GEM
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webrick (1.8.1)
webrick (1.8.2)
websocket (1.2.11)
websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0)
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/collecting_events_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ def collecting_event_params
params.require(:collecting_event).permit(
:verbatim_label, :print_label, :print_label_number_to_print, :document_label,
:verbatim_locality, :verbatim_date, :verbatim_longitude, :verbatim_latitude,
:verbatim_geolocation_uncertainty, :verbatim_trip_identifier, :verbatim_collectors,
:verbatim_geolocation_uncertainty, :verbatim_field_number, :verbatim_collectors,
:verbatim_method, :geographic_area_id, :minimum_elevation, :maximum_elevation,
:elevation_precision, :time_start_hour, :time_start_minute, :time_start_second,
:time_end_hour, :time_end_minute, :time_end_second, :start_date_day,
Expand Down
15 changes: 14 additions & 1 deletion app/controllers/collection_objects_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ class CollectionObjectsController < ApplicationController
:show, :edit, :update, :destroy, :navigation, :containerize,
:depictions, :images, :geo_json, :metadata_badge, :biocuration_classifications,
:timeline,
:api_show, :api_dwc]
:api_show, :api_dwc,
:dwc, :dwc_verbose, :dwc_compact]

after_action -> { set_pagination_headers(:collection_objects) }, only: [:index, :api_index], if: :json_request?

# GET /collecting_events
Expand Down Expand Up @@ -97,6 +99,7 @@ def dwc_index
end

# GET /collection_objects/123/dwc
# !! Returns a keyless Array of data compatible with combining multiple rows
def dwc
o = nil
ActiveRecord::Base.connection_pool.with_connection do
Expand All @@ -115,6 +118,10 @@ def dwc
end

# GET /collection_objects/123/dwc_verbose
#
# !! Always calculates values, never reads from
# !! Allways returns all values
#
def dwc_verbose
o = nil
ActiveRecord::Base.connection_pool.with_connection do
Expand All @@ -130,6 +137,12 @@ def dwc_verbose
render json: o.dwc_occurrence_attributes
end

# GET /collection_objects/123/dwc_compact
# !! Never recalculates !!
def dwc_compact
render json: @collection_object.dwc_occurrence.dwc_json
end

# Intent is DWC fields + quick summary fields for reports
# !! As currently implemented rebuilds DWC all
def report
Expand Down
6 changes: 5 additions & 1 deletion app/helpers/otus_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -328,12 +328,16 @@ def ranked_otu_table(otus)

# @return Hash
# { dwc_occurrence_id: [ image1, image2 ... ], ... }
def dwc_gallery_data(otu)
def dwc_gallery_data(otu, pagination_headers: true)
a = DwcOccurrence.scoped_by_otu(otu)
.select(:id, :dwc_occurrence_object_id, :dwc_occurrence_object_type)
.page(params[:page])
.per(params[:per])

# Somehwhat of a janky pattern, probably needs to be
# moved into Controller.
assign_pagination(a) if pagination_headers

b = Image.with(dwc_scope: a)
.joins("JOIN depictions d on d.image_id = images.id" )
.joins("JOIN dwc_scope on d.depiction_object_id = dwc_scope.dwc_occurrence_object_id AND d.depiction_object_type = 'CollectionObject' AND dwc_scope.dwc_occurrence_object_type = 'CollectionObject'")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</div>

<div class="field label-above">
<label>Delimiter</label>
<label><b>Delimiter</b></label>
<span class="display-block">
<i>Use </i>\n<i> for newlines, </i>\t<i> for tabs.</i>
</span>
Expand All @@ -29,6 +29,15 @@
:options="['Identifier', 'Internal']"
/>
</div>
<div class="field">
<label>
<input
type="checkbox"
v-model="sortBy"
/>
Sort as listed
</label>
</div>
</FacetContainer>
</template>

Expand All @@ -38,6 +47,8 @@ import { vTabkey } from '@/directives'
import VToggle from '@/tasks/observation_matrices/new/components/Matrix/switch.vue'
import FacetContainer from '@/components/Filter/Facets/FacetContainer.vue'
const SORT_BY_VALUE = 'match_identifiers'
const TYPE_PARAMETERS = {
Internal: 'internal',
Identifier: 'identifier'
Expand All @@ -59,6 +70,13 @@ const params = computed({
set: (value) => emit('update:modelValue', value)
})
const sortBy = computed({
get: () => params.value.order_by === SORT_BY_VALUE,
set: (value) => {
params.value.order_by = value ? SORT_BY_VALUE : undefined
}
})
const matchIdentifiers = computed({
get: () => props.modelValue.match_identifiers,
set: (value) => {
Expand Down
56 changes: 50 additions & 6 deletions app/javascript/vue/components/Filter/Table/TableResults.vue
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,30 @@
:key="attr"
@click="sortTable(attr)"
>
<div class="horizontal-left-content">
<div class="horizontal-left-content gap-small">
<span>{{ title }}</span>
<VBtn
color="primary"
circle
@click.stop="
() =>
copyColumnToClipboard(
list
.filter(rowHasCurrentValues)
.map((item) => item[attr])
.join('\n')
)
"
>
<VIcon
name="clip"
x-small
/>
</VBtn>
<VBtn
v-if="filterValues[attr]"
class="margin-small-left"
color="primary"
small
circle
@click.stop="
() => {
delete filterValues[attr]
Expand All @@ -107,13 +124,30 @@
:class="{ 'cell-left-border': pIndex === 0 }"
@click="sortTable(`${key}.${property}`)"
>
<div class="horizontal-left-content">
<div class="horizontal-left-content gap-small">
<span>{{ property }}</span>
<VBtn
color="primary"
circle
@click.stop="
() =>
copyColumnToClipboard(
props.list
.filter(rowHasCurrentValues)
.map((item) => renderItem(item, key, property))
.join('\n')
)
"
>
<VIcon
name="clip"
x-small
/>
</VBtn>
<VBtn
v-if="filterValues[`${key}.${property}`]"
class="margin-small-left"
color="primary"
small
circle
@click.stop="
() => {
delete filterValues[`${key}.${property}`]
Expand Down Expand Up @@ -252,6 +286,7 @@ import { sortArray } from '@/helpers/arrays.js'
import { vResizeColumn } from '@/directives/resizeColumn.js'
import { humanize } from '@/helpers/strings'
import VBtn from '@/components/ui/VBtn/index.vue'
import VIcon from '@/components/ui/VIcon/index.vue'
import HandyScroll from 'vue-handy-scroll'
import RadialNavigation from '@/components/radials/navigation/radial.vue'
import RadialAnnotator from '@/components/radials/annotator/annotator.vue'
Expand Down Expand Up @@ -340,6 +375,15 @@ const dataAttributeHeaders = computed(() => {
const filterValues = ref({})
function copyColumnToClipboard(text) {
navigator.clipboard
.writeText(text)
.then(() => {
TW.workbench.alert.create('Copied to clipboard', 'notice')
})
.catch(() => {})
}
function rowHasCurrentValues(item) {
return Object.entries(filterValues.value).every(([properties, value]) => {
const itemValue = getValue(item, properties)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ import FilterJsonRequestPanel from './FilterJsonRequestPanel.vue'
import PaginationComponent from '@/components/pagination'
import PaginationCount from '@/components/pagination/PaginationCount'
import NavBar from '@/components/layout/NavBar.vue'
import useHotkey from 'vue3-hotkey'
import { useHotkey } from '@/composables'
import platformKey from '@/helpers/getPlatformKey'
import VBtn from '@/components/ui/VBtn/index.vue'
import VIcon from '@/components/ui/VIcon/index.vue'
Expand Down
4 changes: 2 additions & 2 deletions app/javascript/vue/components/pinboard/ModalNavigator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
<script setup>
import VModal from '@/components/ui/Modal'
import Shortcuts from './const/shortcuts.js'
import useHotKey from 'vue3-hotkey'
import { useHotkey } from '@/composables'
import { shorten } from '@/helpers/strings.js'
import { computed, ref, onBeforeMount } from 'vue'
Expand All @@ -81,7 +81,7 @@ const hotkeys = computed(() => {
return keys
})
useHotKey(hotkeys.value)
useHotkey(hotkeys.value)
function redirect() {
const selectedKlass = selected.value.klass
Expand Down
4 changes: 2 additions & 2 deletions app/javascript/vue/components/pinboard/navigator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<script setup>
import platformKey from '@/helpers/getPlatformKey.js'
import useHotKey from 'vue3-hotkey'
import { useHotkey } from '@/composables'
import ModalNavigator from './ModalNavigator.vue'
import { ref, onMounted } from 'vue'
Expand All @@ -25,7 +25,7 @@ const hotkeys = ref([
}
])
useHotKey(hotkeys.value)
useHotkey(hotkeys.value)
const isModalVisible = ref()
const defaultItems = ref({})
const selected = ref()
Expand Down
1 change: 1 addition & 0 deletions app/javascript/vue/composables/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './useOnResize'
export * from './usePopstateListener'
export * from './useScroll'
export * from './useWindowSize'
export * from './useHotkey/useHotkey.js'
56 changes: 56 additions & 0 deletions app/javascript/vue/composables/useHotkey/hotkeyManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { KEY_CODE } from './keycode.js'

class HotKeyManager {
registeredHotkeys = {}
pressedKeys = new Map()

constructor() {
if (typeof window !== 'undefined') {
window.addEventListener('keydown', (e) => {
this.pressedKeys.set(e.key, e.repeat)
const keyComb = this.getKeyComb(Array.from(this.pressedKeys.keys()))
this.registeredHotkeys[keyComb]?.forEach((hotKey) => {
if (!e.repeat || hotKey?.repeat) {
if (hotKey.preventDefault) e.preventDefault()
hotKey.handler([...hotKey.keys], e)
}
})
})
window.addEventListener('keyup', (e) => {
if (this.pressedKeys.has(e.key)) this.pressedKeys.delete(e.key)
})
window.addEventListener('blur', () => {
this.pressedKeys.clear()
})
}
}

registerHotKey = (hotkey) => {
const keyComb = this.getKeyComb([...hotkey.keys])
if (!this.registeredHotkeys[keyComb]) this.registeredHotkeys[keyComb] = []

this.registeredHotkeys[keyComb].push(hotkey)
}

getKeyComb = (keys) => {
const convertKeys = keys.map((item) => {
const stringKey = KEY_CODE[item]
if (stringKey) return stringKey

return item
})
return convertKeys.sort().join(' ')
}

removeHotKey = (hotKey) => {
const keyComb = this.getKeyComb([...hotKey.keys])
const index = this.registeredHotkeys[keyComb]?.indexOf(hotKey) ?? -1
if (index !== -1) {
this.registeredHotkeys[keyComb].splice(index, 1)
return true
}
return false
}
}

export default new HotKeyManager()
18 changes: 18 additions & 0 deletions app/javascript/vue/composables/useHotkey/keycode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const KEY_CODE = {
ctrl: 'Control',
space: ' ',
enter: 'Enter',
tab: 'Tab',
delete: 'Delete',
esc: 'Escape',
alt: 'Alt',
option: 'Alt',
shift: 'Shift',
back: 'Backspace',
meta: 'Meta',
command: 'Meta',
up: 'ArrowUp',
left: 'ArrowLeft',
right: 'ArrowRight',
bottom: 'ArrowDown'
}
Loading

0 comments on commit 66c50fc

Please sign in to comment.