# queryJournal does not have same 'search' feature as Alarm Journal component

**URL:** <https://forum.inductiveautomation.com/t/queryjournal-does-not-have-same-search-feature-as-alarm-journal-component/71124>\
**Category:** Ignition\
**Tags:** vision\
**Created:** [March 15, 2023, 11:11pm UTC](https://forum.inductiveautomation.com/t/queryjournal-does-not-have-same-search-feature-as-alarm-journal-component/71124 "2023-03-15T23:11:15Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![richardNZ15](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/richardnz15/32/33373_2.png) [@richardNZ15](https://forum.inductiveautomation.com/u/richardNZ15)\
**Post date:** [March 15, 2023, 11:11pm UTC](https://forum.inductiveautomation.com/t/queryjournal-does-not-have-same-search-feature-as-alarm-journal-component/71124/1 "2023-03-15T23:11:15Z")

</div>

I am trying to build an export functionality for the exact alarms that are displayed on my Alarm Journal component.  
The following bindings have been utilized on my Alarm Journal component:

- startDate
- endDate
- plus the following:

![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/7/5/7553d641a1bc44d595d34079e2cb746d2cdb569d.png)

Unfortunately it seems the [system.alarm.queryJournal](https://docs.inductiveautomation.com/display/DOC81/system.alarm.queryJournal) function is not set up quite the same way, which is leading to inconsistency between what i can see in the Vision page, and what i am exporting.

First problem is the "Search String" on the Alarm Journal component. The scripting version only offers:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/6/9/695aeeef0aef3755a58459f30f59f86cf9734171.png)

But the handy thing about "Search String" is that it can search in both **source** and **displayPath** simultaneously.

I have a free form text entry component that is bound to "SearchString" to allow people to search for either keywords in displayPath or folders in the tag structure.

I also combine the tag browser tree whcih is bound to the sourceFilter component so that we have more flexibility.

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [March 16, 2023, 1:20am UTC](https://forum.inductiveautomation.com/t/queryjournal-does-not-have-same-search-feature-as-alarm-journal-component/71124/2 "2023-03-16T01:20:47Z")

</div>

You could probably create your export file directly from the table model of the alarm journal rather than trying to recreate its query. I've never attempted anything like that with the alarm journal component specifically, but here is a script I've developed for the alarm status table that exports the alarms to a CSV file on the user's desktop. Perhaps it can be adapted easily to your usage case. It will only export the rows that are being displayed in the alarm status table. It does not export any data that has been filtered out.

Here is the script:

```auto
alarmStatusTable = event.source.parent.getComponent('Alarm Status Table')#path to alarm status table component
import getpass
import time
def getHeaders(table):
	columnList = []
	for column in range(table.getModel().columnCount):
	    columnList.append(table.getModel().getColumnName(column))
	return(columnList)
def getDataset(alarmStatusTable):
	if alarmStatusTable.componentCount > 0:
		for component in alarmStatusTable.getComponents():
			if 'AlarmStatusTable$1' in str(component. __class__ ):
				headers = getHeaders(component)
				model = component.getModel()
				data = []
				for row in range(model.rowCount):
					rowData = []
					for column in range(model.columnCount):
						rowData.append(model.getValueAt(row, column))
					data.append(rowData)
				dataset = system.dataset.toDataSet(headers, data)
				return dataset
			else:
				dataset = getDataset(component)
				if dataset is not None:
					return dataset
	return None
def getFilename():
	current_time = time.localtime()
	username = getpass.getuser()
	filepath = 'C:/Users/' + username + '/Desktop/'
	fileName = 'alarmExport_' + time.strftime('%Y-%m-%d_%H-%M-%S', time.localtime())
	return filepath + fileName
filename = getFilename()
dataset = getDataset(alarmStatusTable)
system.dataset.exportCSV(filename, True, dataset)

```

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [March 16, 2023, 3:14am UTC](https://forum.inductiveautomation.com/t/queryjournal-does-not-have-same-search-feature-as-alarm-journal-component/71124/3 "2023-03-16T03:14:57Z")

</div>

I don't have an alarm journal set up at the moment to test this on, but looking at it, I believe there are only two changes that would be needed to make the above script work on an alarm journal:  
• Correct the path in line one of the code  
• Replace `'AlarmStatusTable$1'` with `'AlarmJournalTable$3'` in the line `if 'AlarmStatusTable$1' in str(component. __class__ ):`

Here is a revised script that I believe will work specifically with the journal table:

> **Revised Alarm Journal Script**
>
> ```auto
> alarmJournalTable = event.source.parent.getComponent('Alarm Journal')#path to alarm status table component
> import getpass
> import time
> def getHeaders(table):
> columnList = []
> for column in range(table.getModel().columnCount):
> columnList.append(table.getModel().getColumnName(column))
> return(columnList)
> def getDataset(alarmJournalTable):
> if alarmJournalTable.componentCount > 0:
> for component in alarmJournalTable.getComponents():
> if 'AlarmJournalTable$3' in str(component. __class__ ):
> headers = getHeaders(component)
> model = component.getModel()
> data = []
> for row in range(model.rowCount):
> rowData = []
> for column in range(model.columnCount):
> rowData.append(model.getValueAt(row, column))
> data.append(rowData)
> dataset = system.dataset.toDataSet(headers, data)
> return dataset
> else:
> dataset = getDataset(component)
> if dataset is not None:
> return dataset
> return None
> def getFilename():
> current_time = time.localtime()
> username = getpass.getuser()
> filepath = 'C:/Users/' + username + '/Desktop/'
> fileName = 'alarmExport_' + time.strftime('%Y-%m-%d_%H-%M-%S', time.localtime())
> return filepath + fileName
> filename = getFilename()
> dataset = getDataset(alarmJournalTable)
> system.dataset.exportCSV(filename, True, dataset)
> 
> ```

---

<div class="post-metadata">

**Author:** ![richardNZ15](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/richardnz15/32/33373_2.png) [@richardNZ15](https://forum.inductiveautomation.com/u/richardNZ15)\
**Post date:** [March 20, 2023, 1:12am UTC](https://forum.inductiveautomation.com/t/queryjournal-does-not-have-same-search-feature-as-alarm-journal-component/71124/4 "2023-03-20T01:12:34Z")

</div>

This works like a charm, thank you!

Is there anyway to access the alarm event object itself?  
Like how in the results of the system.alarm.queryJournal function you can do alarm.get(propName) to get any property you want.

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [March 20, 2023, 3:28am UTC](https://forum.inductiveautomation.com/t/queryjournal-does-not-have-same-search-feature-as-alarm-journal-component/71124/5 "2023-03-20T03:28:45Z")

</div>

> [@richardNZ15](#):
>
> Is there anyway to access the alarm event object itself?

Column zero of the journal table model will always be the event ID regardless of how the user has arranged the journal table. Therefore, you could do something like this:

```auto
eventID = model.getValueAt(row, 0)
prop = [("EventId","=", eventID)]
alarmArray = system.alarm.queryJournal(all_properties = prop)
alarm = alarmArray[0]

```

What is returned by the journal query will actually be an iterable array. However, since in this example, you've structured your query in a way that will only return one result, you can immediately access the alarm object by adding `[0]` to the end of the array. In this regard, queryJournal and queryStatus both work the same.

Once you have the alarm object you an access any of the properties with the following map:

```auto
ackNotes = alarm.getOrElse('ackNotes', None)
ackTime = alarm.getOrElse('eventTime', None) if alarm.acked else None
ackedBy = alarm.getOrElse('ackUser', None)
activePipeline = alarm.getOrElse('activePipeline', None)
clearedPipeline = alarm.getOrElse('clearPipeline', None)
ackPipeline = alarm.getOrElse('ackPipeline', None)
activeTime = alarm.getOrElse('activeTime', None)
clearTime = alarm.getOrElse('clearTime', None)
eventValue = alarm.getOrElse('eventValue', None)
deadBand = alarm.getOrElse('deadband', None)
displayPath = alarm.displayPath #This also Works:.getDisplayPath() AND This also works: .getOrElse('displayPath', None)
acked = alarm.acked #This also works: .getOrElse('isAcked', None)
active = alarm.getOrElse('isActive', None) #In my experimentation, .active didn't work
clear = alarm.cleared #This also works: .getOrElse('isClear', None)
name = alarm.name #This also works.getName() AND This also works: .getOrElse('name', None)
notes = alarm.notes #This also works: .getNotes() AND This also works: .getOrElse('notes', None)
priority = alarm.priority #This also works: .getPriority() AND This also works:.getOrElse('priority', None)
sourcePath = alarm.source #This also works: getSource() AND This also works: .getOrElse('source', None)
currentState = alarm.state #This also works: .getState() #The following DID NOT return the expected result: .getOrElse('eventState', None)
eventID = alarm.id #This also works: .getID()
unacknowledgedDuration = alarm.getOrElse('ackDuration', None)
activeDuration = alarm.getOrElse('activeDuration', None)
label = alarm.label

```

Note, if you have structured your alarm query in a way that will return more than one result, you will need to iterate through it to get all of the alarm objects.

**Example:**

```auto
for alarm in alarmArray:
	alarmObject = alarmArray[alarm]

```

---

<div class="post-metadata">

**Author:** ![richardNZ15](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/richardnz15/32/33373_2.png) [@richardNZ15](https://forum.inductiveautomation.com/u/richardNZ15)\
**Post date:** [March 20, 2023, 10:04am UTC](https://forum.inductiveautomation.com/t/queryjournal-does-not-have-same-search-feature-as-alarm-journal-component/71124/6 "2023-03-20T10:04:57Z")

</div>

Thanks,  
I'd be a bit concerned with calling system.alarm.queryJournal for X amount of times where X is the number of events in the table component. We could easily get into thousands of items and I do not want to bog it down too much.

It seems like it would be better to start with the full results from the queryJournal in that regard, and build up the dataset from that.  
Something like:

```auto
	for alarm in journalResults:
		# NOTE: Casting alarm.getPriority() as string shows string equivilant instead of decimal
		data.append([alarm.get(eventTimeObject), alarm.getId(),alarm.getSource(),alarm.getDisplayPath(),alarm.getName(),alarm.getState(),str(alarm.getPriority()),alarm.getOrElse("AlarmClass",None),alarm.getOrElse("AlarmType",None),alarm.getOrElse("TagType", None)])
		
	# end for
	journalData = system.dataset.toDataSet(headers, data)

```

But then it's back to the difficulties of ensuring the filters match. I'll sleep on it and see if any inspiration strikes.

---

<div class="post-metadata">

**Author:** ![justinedwards.jle](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/justinedwards.jle/32/37854_2.png) [@justinedwards.jle](https://forum.inductiveautomation.com/u/justinedwards.jle)\
**Post date:** [March 20, 2023, 2:15pm UTC](https://forum.inductiveautomation.com/t/queryjournal-does-not-have-same-search-feature-as-alarm-journal-component/71124/7 "2023-03-20T14:15:23Z")

</div>

> [@richardNZ15](#):
>
> We could easily get into thousands of items and I do not want to bog it down

Agreed. That would be a big performance problem, and it doesn't make sense to build a dataset from a table that already provides one. For me, it would be much simpler to filter unwanted columns from the table's existing data than to build a new dataset from scratch because I know that the order of the column model doesn't change no matter how the user arranges the table and no matter which columns the user decides to hide or show.

That said, if I were building a custom journal table from scratch, I would probably build a list of properties first, so I could obtain my dataset from a single query.
