Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update/populateinstruments #106

Merged
merged 7 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
22 changes: 3 additions & 19 deletions src/components/RealTimeInterface/SessionStarted.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ import { calcAltAz } from '../../utils/visibility.js'
import { useRealTimeSessionsStore } from '../../stores/realTimeSessions'
import sites from '../../utils/sites.JSON'
import { fetchApiCall } from '../../utils/api'
import { getFilterList } from '../../utils/populateInstrumentsUtils'
import { useConfigurationStore } from '../../stores/configuration'
import { useUserDataStore } from '../../stores/userData'

const realTimeSessionsStore = useRealTimeSessionsStore()
const configurationStore = useConfigurationStore()
const userDataStore = useUserDataStore()

const isCapturingImages = computed(() => {
if (configurationStore.demo == true) {
Expand Down Expand Up @@ -144,21 +143,6 @@ const sendGoCommand = async () => {
})
}

// This should change from configdbUrl/opticalelementgroups/128/ to
// https://observe.lco.global/api/instruments and map the instruments based on what telescope is being used
const getFilterList = async () => {
await fetchApiCall({
url: configurationStore.configdbUrl + 'opticalelementgroups/128/',
method: 'GET',
successCallback: (data) => {
filterList.value = data.optical_elements
.filter(filter => filter.schedulable)
.map(filter => ({ name: filter.name, code: filter.code }))
},
failCallback: (error) => { console.error('API failed with error', error) }
})
}

function updateRenderGallery (value) {
if (!value) {
realTimeSessionsStore.updateImageCaptureState(false)
Expand All @@ -174,9 +158,9 @@ watch(exposureTime, (newTime) => {
exposureError.value = ''
})

onMounted(() => {
onMounted(async () => {
loading.value = false
getFilterList()
filterList.value = await getFilterList()
})

</script>
Expand Down
25 changes: 3 additions & 22 deletions src/components/Scheduling/SchedulingSettings.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup>
import { ref, reactive, computed, defineProps, defineEmits, onMounted } from 'vue'
import { useConfigurationStore } from '../../stores/configuration.js'
import { fetchApiCall } from '../../utils/api.js'
import { getFilterList } from '../../utils/populateInstrumentsUtils.js'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'

const props = defineProps({
Expand Down Expand Up @@ -36,25 +36,6 @@ const settings = reactive({
count: ''
})

const getFilterList = async () => {
await fetchApiCall({
url: configurationStore.observationPortalUrl + 'instruments',
method: 'GET',
successCallback: (data) => {
Object.values(data).forEach(instrument => {
// Checks if the instrument has a "class" of "0m4" and contains filters in optical_elements
if (instrument.class === '0m4' && instrument.optical_elements.filters) {
const schedulableFilters = instrument.optical_elements.filters
.filter(filter => filter.schedulable)
.map(filter => ({ name: filter.name, code: filter.code }))
filterList.value.push(...schedulableFilters)
}
})
},
failCallback: (error) => { console.error('API failed with error', error) }
})
}

// State for enabling/disabling fields
const targetEnabled = computed(() => {
return props.showProjectField ? projectName.value.trim() !== '' : true
Expand Down Expand Up @@ -139,8 +120,8 @@ const formatExposures = (exposures) => {
return exposures.map(exposure => `${exposure.filterName} - ${exposure.exposureTime}s x ${exposure.count}`).join(', ')
}

onMounted(() => {
getFilterList()
onMounted(async () => {
filterList.value = await getFilterList()
})

</script>
Expand Down
75 changes: 0 additions & 75 deletions src/tests/integration/components/schedulingSettings.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,6 @@ describe('SchedulingSettings.vue', () => {
fetchApiCall.mockClear()
const { pinia } = createTestStores()

fetchApiCall.mockImplementationOnce(({ successCallback }) => {
if (successCallback) {
successCallback({
'0M4-SCICAM-QHY600': {
'class': '0m4',
'optical_elements': {
'filters': [
{ name: 'Filter A', code: 'FA', schedulable: true },
{ name: 'Filter B', code: 'FB', schedulable: false }
]
}
},
'0M4-SCICAM-FLI': {
'class': '0m4',
'optical_elements': {
'filters': [
{ name: 'Filter D', code: 'FD', schedulable: true },
{ name: 'Filter E', code: 'FE', schedulable: true }
]
}
}
})
}
})

wrapper = mount(SchedulingSettings, {
global: {
plugins: [pinia],
Expand All @@ -53,28 +28,6 @@ describe('SchedulingSettings.vue', () => {
})
})

it('calls fetchApiCall to get filter list on mount', async () => {
expect(fetchApiCall).toHaveBeenCalledWith(
expect.objectContaining({
url: 'http://mock-api.com/instruments',
method: 'GET',
successCallback: expect.any(Function),
failCallback: expect.any(Function)
})
)

await flushPromises()

// filters through the non-schedulable filters
expect(wrapper.vm.filterList).toEqual([
{ name: 'Filter A', code: 'FA' },
{ name: 'Filter D', code: 'FD' },
{ name: 'Filter E', code: 'FE' }
])

expect(fetchApiCall).toHaveBeenCalledTimes(1)
})

it('fetches RA and Dec based on the target name and updates state correctly', async () => {
const mockRaDecResponse = {
'dec': '+41 16 07.50',
Expand Down Expand Up @@ -116,32 +69,4 @@ describe('SchedulingSettings.vue', () => {

expect(wrapper.vm.targetError).toBe('Target not found, try another target.')
})

it('emits exposuresUpdated when exposures are added', async () => {
// Checks that filterList has been populated correctly
expect(wrapper.vm.filterList).toEqual([
{ name: 'Filter A', code: 'FA' },
{ name: 'Filter D', code: 'FD' },
{ name: 'Filter E', code: 'FE' }
])

// Simulates selecting a filter from the dropdown
wrapper.vm.settings.filter = 'FA'
wrapper.vm.settings.filterName = 'Filter A'
wrapper.vm.settings.exposureTime = '30'
wrapper.vm.settings.count = '15'

await wrapper.vm.addExposure()

// Check that the event is emitted with the expected payload
expect(wrapper.emitted().exposuresUpdated).toBeTruthy()
expect(wrapper.emitted().exposuresUpdated[0][0]).toEqual([
{
filter: 'FA',
filterName: 'Filter A',
exposureTime: '30',
count: '15'
}
])
})
})
89 changes: 89 additions & 0 deletions src/tests/unit/utils/populateInstrumentsUtils.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { vi, describe, it, expect, beforeEach } from 'vitest'
import { getFilterList } from '../../../utils/populateInstrumentsUtils.js'
import { fetchApiCall } from '../../../utils/api.js'
import { createTestStores } from '../../../utils/testUtils'

vi.mock('@/utils/api.js', () => ({
fetchApiCall: vi.fn()
}))

describe('thumbnailsUtils.js', () => {
let configurationStore

beforeEach(() => {
const { configurationStore: store } = createTestStores()
configurationStore = store
fetchApiCall.mockClear()
})

describe('getFilterList', () => {
beforeEach(() => {
fetchApiCall.mockClear()
})

it('fetches instruments and returns schedulable filters', async () => {
// Mock API response
fetchApiCall.mockResolvedValueOnce({
'0M4-SCICAM-QHY600': {
class: '0m4',
optical_elements: {
filters: [
{ name: 'Filter A', code: 'FA', schedulable: true },
{ name: 'Filter B', code: 'FB', schedulable: false }
]
}
},
'0M4-SCICAM-FLI': {
class: '0m4',
optical_elements: {
filters: [
{ name: 'Filter D', code: 'FD', schedulable: true },
{ name: 'Filter E', code: 'FE', schedulable: true }
]
}
},
// This instrument should be ignored
'1M0-SCICAM': {
class: '1m0',
optical_elements: {
filters: [
{ name: 'Filter X', code: 'FX', schedulable: true }
]
}
}
})

const result = await getFilterList()

expect(result).toEqual([
{ name: 'Filter A', code: 'FA' },
{ name: 'Filter D', code: 'FD' },
{ name: 'Filter E', code: 'FE' }
])

expect(fetchApiCall).toHaveBeenCalledWith(
expect.objectContaining({
url: expect.stringContaining('instruments'),
method: 'GET'
})
)
})

it('returns an empty array if no schedulable filters are found', async () => {
fetchApiCall.mockResolvedValueOnce({
'0M4-SCICAM-QHY600': {
class: '0m4',
optical_elements: {
filters: [
{ name: 'Filter A', code: 'FA', schedulable: false }
]
}
}
})

const result = await getFilterList()

expect(result).toEqual([])
})
})
})
20 changes: 20 additions & 0 deletions src/utils/populateInstrumentsUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { fetchApiCall } from './api.js'
import { useConfigurationStore } from '../stores/configuration.js'

export const getFilterList = async () => {
const configurationStore = useConfigurationStore()
const response = await fetchApiCall({
url: `${configurationStore.observationPortalUrl}instruments`,
method: 'GET'
})
const filterList = []
Object.values(response).forEach((instrument) => {

Check failure on line 11 in src/utils/populateInstrumentsUtils.js

View workflow job for this annotation

GitHub Actions / test

Unhandled error

TypeError: Cannot convert undefined or null to object ❯ Module.getFilterList src/utils/populateInstrumentsUtils.js:11:10 ❯ processTicksAndRejections node:internal/process/task_queues:95:5 ❯ src/components/Scheduling/SchedulingSettings.vue:124:22 This error originated in "src/tests/integration/components/schedulingSettings.test.js" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "fetches RA and Dec based on the target name and updates state correctly". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.

Check failure on line 11 in src/utils/populateInstrumentsUtils.js

View workflow job for this annotation

GitHub Actions / test

Unhandled error

TypeError: Cannot convert undefined or null to object ❯ Module.getFilterList src/utils/populateInstrumentsUtils.js:11:10 ❯ src/components/Scheduling/SchedulingSettings.vue:124:22 This error originated in "src/tests/integration/components/schedulingSettings.test.js" test file. It doesn't mean the error was thrown inside the file itself, but while it was running. The latest test that might've caused the error is "displays an error if target name is invalid". It might mean one of the following: - The error was thrown, while Vitest was running this test. - If the error occurred after the test had been completed, this was the last documented test before it was thrown.
if (instrument.class === '0m4' && instrument.optical_elements.filters) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this is probably fine for now, but if Wayne's telescopes are integrated they may be 0m35 or something different, and we generally would want to keep this easily adjustable. You could put it in a global at the top of the file, or read it from the public/config/config.json which we can set in the deployment at runtime!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

oo I like that!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm intrigued by this read it from the public/config/config.json which we can set in the deployment at runtime. I'll be declaring it as a global config and ask you about that statement in person (or you can also explain here but I'm patient and willing to rewrite this when the time comes)

const schedulableFilters = instrument.optical_elements.filters
.filter(filter => filter.schedulable)
.map(filter => ({ name: filter.name, code: filter.code }))
filterList.push(...schedulableFilters)
}
})
return filterList
}
Loading