class ProgramStageInstanceSupplier extends AbstractSupplier<Map<String, ProgramStageInstance>> {

    @Override
    public Map<String, ProgramStageInstance> get( ImportOptions importOptions, List<Event> events )
    {
        if ( events == null )
        {
            return new HashMap<>();
        }

        Set<String> psiUid = events.stream().map( Event::getUid ).collect( Collectors.toSet() );

        if ( isEmpty( psiUid ) )
        {
            return new HashMap<>();
        }

        final String sql = "select psi.programinstanceid, psi.programstageid, psi.programstageinstanceid, " +
            "psi.uid, psi.status, psi.deleted, psi.eventdatavalues, psi.duedate, psi.executiondate, " +
            "psi.completeddate, psi.attributeoptioncomboid, psi.geometry, " +
            "ou.organisationunitid, ou.uid, ou.code, ou.name, psi.attributeoptioncomboid,  c.uid as coc_uid  " +
            "from programstageinstance psi join organisationunit ou on psi.organisationunitid = ou.organisationunitid "
            +
            "join categoryoptioncombo c on psi.attributeoptioncomboid = c.categoryoptioncomboid " +
            "where psi.uid in (:ids)";

        MapSqlParameterSource parameters = new MapSqlParameterSource();
        parameters.addValue( "ids", psiUid );

        return jdbcTemplate.query( sql, parameters, rs -> {
            Map<String, ProgramStageInstance> results = new HashMap<>();

            while ( rs.next() )
            {
                ProgramStageInstance psi = new ProgramStageInstance();

                psi.setId( rs.getLong( "programstageinstanceid" ) );
                psi.setUid( rs.getString( "uid" ) );
                psi.setStatus( EventStatus.valueOf( rs.getString( "status" ) ) );
                psi.setDeleted( rs.getBoolean( "deleted" ) );
                psi.setProgramStage( getProgramStage( importOptions, rs.getLong( "programstageid" ) ) );
                psi.setOrganisationUnit( getOu( rs ) );
                psi.setDueDate( rs.getDate( "duedate" ) );
                psi.setExecutionDate( rs.getDate( "executiondate" ) );
                psi.setCompletedDate( rs.getDate( "completeddate" ) );
                psi.setAttributeOptionCombo( getCatOptionCombo ( rs ));
                try
                {
                    psi.setEventDataValues( EventUtils.jsonToEventDataValues( jsonMapper, rs.getObject(
                        "eventdatavalues" ) ) );
                }
                catch ( JsonProcessingException e )
                {
                    log.error(
                        "Invalid Data Element Values payload, skipping Program Stage Instance with id: " + psi.getId(),
                        e );
                }
                results.put( psi.getUid(), psi );
            }
            return results;
        } );
    }

    public  ProgramStageInstanceSupplier( NamedParameterJdbcTemplate jdbcTemplate,
        @Qualifier( "dataValueJsonMapper" ) ObjectMapper jsonMapper, ProgramSupplier programSupplier );

}