Create list of only non-empty/non-null dictionaries - python-3.x

I'm creating a model in Pydantic to get a JSON-like format from a dataframe.
What I have so far is this:
from datetime import datetime
from pydantic import BaseModel
from typing import Optional, List, Deque, Union
from collections.abc import Sequence
class startDate (BaseModel):
year: int
month: int
day: int
class endDate (BaseModel):
year: int
month: int
day: int
class origin(BaseModel):
id: str
class destinations(BaseModel):
id: Optional[str] = None
class transfers_list(BaseModel):
unloadingDuration: Optional[str] = None
destination: Optional[destinations] = None
class frequency(BaseModel):
id: str
class routeDetails (BaseModel):
arrivalTime: str
loadingStartTime: str
loadingDuration: str
releaseVehicleDuration: str
vehicleType: str
carrierId: str
returnRoute: bool
dock: Optional[str]
origin: origin
transfers: Optional[list[transfers_list]] = None
frequency: frequency
class main (BaseModel):
region: str
startDate: startDate
endDate: endDate
recurrenceDays: list[int]
routeDetails: routeDetails
class head (BaseModel):
recurrenceSchedule: main
m = main(region='ABC',
startDate={'year': 2023,
'month': 1,
'day': 1},
endDate={'year': 2023,
'month': 1,
'day': 1},
recurrenceDays=[5],
routeDetails = {'arrivalTime':'09:00:00',
'loadingStartTime':'09:00:00',
'loadingDuration':'09:00:00',
'releaseVehicleDuration':'09:00:00',
'vehicleType':"truck",
'carrierId':"carrier_name",
'returnRoute':True,
'dock': None,
'origin': origin(id="NYC"),
'transfers': [transfers_list(unloadingDuration='10s', destination=destinations(id='id_destination')),transfers_list(unloadingDuration=None, destination=destinations(id=None))],
'frequency': frequency(id='247')
}
)
final = (head(recurrenceSchedule = m))
final = final.json(exclude_unset=True,exclude_defaults=True)
print(final)
And the Output is this:
{
"recurrenceSchedule":{
"region":"ABC",
"startDate":{
"year":2023,
"month":1,
"day":1
},
"endDate":{
"year":2023,
"month":1,
"day":1
},
"recurrenceDays":[
5
],
"routeDetails":{
"arrivalTime":"09:00:00",
"loadingStartTime":"09:00:00",
"loadingDuration":"09:00:00",
"releaseVehicleDuration":"09:00:00",
"vehicleType":"truck",
"carrierId":"carrier_name",
"returnRoute":true,
"origin":{
"id":"NYC"
},
"transfers":[
{
"unloadingDuration":"10s",
"destination":{
"id":"id_destination"
}
},
{
"destination":{
}
}
],
"frequency":{
"id":"247"
}
}
}
}
This is almost exactly what I need, but in my Dataframe I have other columns for unloadingDuration and Destination that are inside "Transfers". However, when the value in the DF is empty/null/None, I don't want another dictionary inside the list. My desired output is:
"transfers":[
{
"unloadingDuration":"10s",
"destination":{
"id":"id_destination"
}
}
]
Instead of this:
"transfers":[
{
"unloadingDuration":"10s",
"destination":{
"id":"id_destination"
}
},
{
"destination":{
}
}
]
I have tried changing my NaN fields (unloadingDuration and destination) in the dataframe to None, but this doesn't work.
I tried changing some classes to Optional back and forth and this didn't seem to work either.
I also used exclude_unset=True and exclude_defaults=True that do work in some cases (dock is Optional, and when I set it to null it simply doesn't show up).

Try excluding none values:
final = final.json(exclude_unset=True, exclude_none=True)

Related

FetchRequest based on subset criteria

Consider the following object graph:
#objcMembers class Event: NSObject {
let name: String
let sections: [Section]
init(name: String, sections: [Section]) {
self.name = name
self.sections = sections
}
}
#objcMembers class Section: NSObject {
let name: String
let videos: [Video]
init(name: String, videos: [Video]) {
self.name = name
self.videos = videos
}
}
#objcMembers class Video: NSObject {
let name: String
let isFavorite: Bool
let isWatched: Bool
init(name: String, isFavorite: Bool, isWatched: Bool) {
self.name = name
self.isFavorite = isFavorite
self.isWatched = isWatched
}
}
I would like to filter the event sections which only have the videos marked as favorites
let events = [Event(name: "2021 Event",
sections: [
Section(name: "Monday",
videos: [Video(name: "VideoMonday1",
isFavorite: true,
isWatched: true),
Video(name: "VideoMonday2",
isFavorite: false,
isWatched: true)]),
Section(name: "Tuesday",
videos: [Video(name: "VideoTuesday1",
isFavorite: false,
isWatched: false),
Video(name: "VideoTuesday2",
isFavorite: true,
isWatched: true)])
])
]
//Expected result
let filtered = [Event(name: "2021 Event",
sections: [
Section(name: "Monday",
videos: [Video(name: "VideoMonday1",
isFavorite: true,
isWatched: true)]),
Section(name: "Tuesday",
videos: [Video(name: "VideoTuesday2",
isFavorite: true,
isWatched: true)])
])
]
Im using CoreData, and i came up with this predicate:
let favPredicate = NSPredicate(format: "SUBQUERY(sections, $sect, SUBQUERY($sect.videos, $vid, $vid.isFavorite == YES).#count > 0).#count > 0")
This however doesnt exclude the videos which are not favorites, from the sections.
It s a query which runs on the event entity, and "it works" because it filters the array, but not what im looking for.
I can achieve this as soon as i get the root array: however, i d like to accomplish this using coreData, by not loading not needed objects in memory. Is this possible or its not worth in terms of complexity?

Same value was passed as the nextKey in two sequential Pages loaded from a PagingSource in Paging Library 3 Android

I migrated from paging 2 to paging 3. I tried to implement ItemKeyedDataSource of Paging 2 to Paging library 3. But the problem I was facing is, the same value(currentJodId) was passed as the nextkey in two sequential Pages loaded. And After that app crashes. but if I add "keyReuseSupported = true" in DataSource, app does not crash. But it started calling same item id as the nextkey.
JobSliderRestApi.kt
#GET("job/list/slides")
fun getDetailOfSelectedJob(
#Query("current_job") currentJodId: Int?,
#Query("limit") jobLimit: Int?,
#Query("search_in") fetchType: String?
): Single<Response<JobViewResponse>>
JobViewResponse.kt
data class JobViewResponse(
#SerializedName("data") val data: ArrayList<JobDetail>?
) : BaseResponse()
JobDetail.kt
data class JobDetail(
#SerializedName("job_id") val jobId: Int,
#SerializedName("tuition_type") val jobType: String?,
#SerializedName("class_image") val jobImage: String,
#SerializedName("salary") val salary: String,
#SerializedName("no_of_student") val noOfStudent: Int,
#SerializedName("student_gender") val studentGender: String,
#SerializedName("tutor_gender") val preferredTutor: String,
#SerializedName("days_per_week") val daysPerWeek: String?,
#SerializedName("other_req") val otherReq: String?,
#SerializedName("latitude") val latitude: Double?,
#SerializedName("longitude") val longitude: Double?,
#SerializedName("area") val area: String,
#SerializedName("tutoring_time") val tutoringTime: String?,
#SerializedName("posted_date") val postedDate: String?,
#SerializedName("subjects") val subjects: String,
#SerializedName("title") val title: String
)
JodSliderDataSource.kt
class JodSliderDataSource #Inject constructor(
private val jobSliderRestApi: JobSliderRestApi
): RxPagingSource<Int, JobDetail>() {
// override val keyReuseSupported = true
#ExperimentalPagingApi
override fun getRefreshKey(state: PagingState<Int, JobDetail>): Int? {
return state.anchorPosition?.let {
state.closestItemToPosition(it)?.jobId
}
}
override fun loadSingle(params: LoadParams<Int>): Single<LoadResult<Int, JobDetail>> {
return jobSliderRestApi.getDetailOfSelectedJob(42673, 2, "next").toSingle()
.subscribeOn(Schedulers.io())
.map { jobResponse -> toLoadResult(jobResponse.data) }
.onErrorReturn { LoadResult.Error(it) }
}
private fun toLoadResult(data: ArrayList<JobDetail>): LoadResult<Int, JobDetail> {
return LoadResult.Page(data = data, prevKey = null, nextKey = data.lastOrNull()?.jobId)
}
}
I was getting the same error and this is what worked for me. In the JodSliderDataSource class, toLoadResult method, set the nextKey parameter value by getting the page number from the response data and adding one.
private fun toLoadResult(
data: ArrayList<JobDetail>
): LoadResult<Int, JobDetail> {
return LoadResult.Page(
data = data,
prevKey = null,
nextKey = data.lastOrNull()?.jobId + 1 // Add one to the page number here.
)
}

Compare all Fields of an Object

In our Integration Tests we wan't to compare every field of an Object returned by an Rest Controller with an object constructed in the test.
This example illustrates the problem:
class RestIntegrationTest extends Specification {
def "Should return contracts"() {
when:
def actual = callRestController()
then:
// compare all fields of actual with "contract"
actual == new Contract(
number: "123",
signDate: "2017-04-01",
address: new Address(
name: "Foobar",
street: "Foostreet",
city: "Frankfurt",
zip: "60486"
),
persons: [new Person(name: "Christian")]
)
}
def callRestController() {
return new Contract(
number: "123",
signDate: "2017-04-01",
address: new Address(
name: "Foobar",
street: "Wrong Street",
city: "Frankfurt",
zip: "60486"
),
persons: [new Person(name: "Frank")]
)
}
static class Contract {
String number
String signDate
Address address
Person[] persons
}
static class Address {
String name
String street
String city
String zip
}
static class Person {
String name
}
}
As output we like expect something like this:
address.street "Wrong Street" != "Foostreet"
persons[0].name "Christian" != "Frank"
Breaking the assert into multiple "==" lines would lead into the correct output, but that will be not handy since some objects are quite huge.
You can try the groovy's #EqualsAndHashCode:
import groovy.transform.EqualsAndHashCode
#EqualsAndHashCode
static class Address {
String name
String street
String city
String zip
}
You can use unitils assertReflectionEquals
http://unitils.sourceforge.net/tutorial-reflectionassert.html
It's not comprehensive but may be sufficient for your needs:
def compareFields( obj1, obj2, propName = null ) {
obj1.properties.each {
if ( it.value instanceof Object[] ) {
def obj2Len = obj2."${it.key}".length
it.value.eachWithIndex { collObj, idx ->
if ( idx + 1 <= obj2Len )
compareFields( collObj, obj2."${it.key}"[idx], "${it.key}[${idx}]" )
}
}
if ( !it.value.class.getCanonicalName().contains( 'java' ) ) {
compareFields( it.value, obj2."${it.key}", it.key )
}
if ( it.value.class.getCanonicalName().contains( 'java' ) &&
it.key != 'class' &&
it.value <=> obj2."${it.key}") {
println "${propName ? "$propName." : ''}${it.key}: '${it.value}' != '" + obj2."${it.key}" + "'"
}
}
}

groovy object with trait to json conversion

I am trying to convert object to JSON. Object has a trait which supposed to convert object. But I get weird json result.
import groovy.json.*
trait JsonPackageTrait {
def toJson() {
JsonOutput.prettyPrint(
JsonOutput.toJson(this)
)
}
}
class Item {
def id, from, to, weight
}
def item = new Item()
item.with {
id = 1234512354
from = 'London'
to = 'Liverpool'
weight = 15d.lbs()
}
item = item.withTraits JsonPackageTrait
println item.toJson()
JSON result
{
"from": "London",
"id": 1234512354,
"to": "Liverpool",
"proxyTarget": {
"from": "London",
"id": 1234512354,
"to": "Liverpool",
"weight": 33.069
},
"weight": 33.069
}
So it seems I cannot do it like this?
Well, whatever. As using withTraits leads to creating proxy of the original object I resolved like this for my current implementation
trait JsonPackageTrait {
def toJson() {
JsonOutput.prettyPrint(
JsonOutput.toJson(this.$delegate)
)
}
}

JTable not updating when its content changes

import groovy.beans.Bindable;
import groovy.swing.SwingBuilder
import java.awt.BorderLayout
import javax.swing.JFrame
#Bindable def people = [[name:"Mary", age:18],
[name:"Tom", age:25]]
def swingBuilder = new SwingBuilder()
swingBuilder.edt { // edt method makes sure UI is build on Event Dispatch Thread.
lookAndFeel 'system' // Simple change in look and feel.
frame( title: 'Display a table',
size: [400,300],
show: true,
locationRelativeTo: null,
defaultCloseOperation: JFrame.EXIT_ON_CLOSE)
{
menuBar() {
menu(text: "File", mnemonic: 'F'){
menuItem(text: "Add",
actionPerformed: {
people << [name:"Harry", age:17]
println people
}
)}
}
panel(layout: new BorderLayout()) {
scrollPane(constraints: BorderLayout.CENTER) {
table() {
def listTeacher
tableModel(list: people) {
propertyColumn(header: 'Name', propertyName: 'name')
propertyColumn(header: 'Age', propertyName: 'age')
}
}
}
}
}
}
When click "Add" one entry is added to the list people but the JTable is not updated. How can I fix it?
You can make it an ObservableList, and then refresh the table model when the list changes in some way:
import groovy.beans.Bindable;
import groovy.swing.SwingBuilder
import java.awt.BorderLayout
import javax.swing.JFrame
import java.beans.PropertyChangeListener
#Bindable ObservableList people = [ [name:"Mary", age:18],
[name:"Tom", age:25] ]
def swingBuilder = new SwingBuilder()
swingBuilder.edt { // edt method makes sure UI is build on Event Dispatch Thread.
lookAndFeel 'system' // Simple change in look and feel.
frame( title: 'Display a table',
size: [400,300],
show: true,
locationRelativeTo: null,
defaultCloseOperation: JFrame.HIDE_ON_CLOSE) {
menuBar() {
menu(text: "File", mnemonic: 'F'){
menuItem(text: "Add", actionPerformed: {
people << [name:"Harry", age:17]
println people
}
) }
}
panel(layout: new BorderLayout()) {
scrollPane(constraints: BorderLayout.CENTER) {
table {
def listTeacher
tableModel( id:'model', list: people) { m ->
propertyColumn(header: 'Name', propertyName: 'name')
propertyColumn(header: 'Age', propertyName: 'age')
}
}
}
people.addPropertyChangeListener( { e -> model.fireTableDataChanged() } as PropertyChangeListener )
}
}
}

Resources