Skip to content

Commit

Permalink
Fixed some review issues.
Browse files Browse the repository at this point in the history
  • Loading branch information
twalmsley committed Feb 13, 2024
1 parent 34dc9f6 commit 5f038ee
Show file tree
Hide file tree
Showing 2 changed files with 319 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
package uk.gov.gchq.magmacore.examples.functional;

import java.time.Instant;
import java.util.UUID;
import java.util.function.Function;

import uk.gov.gchq.magmacore.hqdm.model.Activity;
import uk.gov.gchq.magmacore.hqdm.model.ClassOfPerson;
import uk.gov.gchq.magmacore.hqdm.model.KindOfActivity;
import uk.gov.gchq.magmacore.hqdm.model.Person;
import uk.gov.gchq.magmacore.hqdm.model.PointInTime;
import uk.gov.gchq.magmacore.hqdm.model.Role;
import uk.gov.gchq.magmacore.hqdm.model.StateOfPerson;
import uk.gov.gchq.magmacore.hqdm.rdf.iri.HQDM;
import uk.gov.gchq.magmacore.hqdm.rdf.iri.IRI;
import uk.gov.gchq.magmacore.hqdm.rdf.iri.IriBase;
import uk.gov.gchq.magmacore.hqdm.rdf.iri.RDFS;
import uk.gov.gchq.magmacore.hqdm.services.ClassServices;
import uk.gov.gchq.magmacore.hqdm.services.SpatioTemporalExtentServices;
import uk.gov.gchq.magmacore.service.MagmaCoreService;
import uk.gov.gchq.magmacore.service.MagmaCoreServiceFactory;

/**
* A Functional Programming example for using MagmaCore.
*
* <p>
* The purpose of this example is to try to write a program that is more readable than the other examples.
* The {@link #main() main} method shows a clear sequence of steps carried out by the use case, and it
* should be possible to write the steps as reusable and composable functions.
* </p>
*/
public class FunctionalProgrammingExample {

/**
* A program showing how to use functional programming with MagmaCore.
*/
public static void main(final String[] args) {

/*
* Use function composition to build up a program that we will run later.
* The program will use a Context object to keep track of entities that
* are created. All functions in the processing chain accept a Context,
* mutate it, then return it. Ideally a `record` would be used instead
* and Lombok could be used to add 'wither' methods so that the Context
* could be immutable.
*/
final Function<Context, Context> program =

/*
* First create a MagmaCoreService in the Context.
*/
createMagmaCoreService

/*
* The first transaction will populate the Reference Data needed by this example.
* Normally such data will already exist in the database and this step will not
* be necessary.
*/
.andThen(beginWriteTransaction)
.andThen(populateRefData)
.andThen(commitTransaction)

/*
* Often a program will need to look up some existing entities for Reference
* Data needed by the use case. In this case they are stored in the Context.
*/
.andThen(beginReadTransaction)
.andThen(findRefData)
.andThen(commitTransaction)

/*
* New entities can be created making use of the Reference Data. No transaction
* is needed since the entities will be persisted at the end.
*/
.andThen(createPerson)
.andThen(createResearchActivity)
.andThen(createPersonAsParticipantInActivity)

/*
* The last transaction persists all of the new entities.
*/
.andThen(beginWriteTransaction)
.andThen(creatEntities)
.andThen(commitTransaction);

/*
* Now execute the program created above.
*/
final Context ctx = program.apply(new Context());

/*
* Check that the results are as expected.
*/
if (ctx.magmaCore.get(ctx.person.getId()) == null) {
System.err.println("Cannot find person object with IRI: " + ctx.person.getId());
}
if (ctx.magmaCore.get(ctx.researchActivity.getId()) == null) {
System.err.println("Cannot find researchActivity object with IRI: " + ctx.researchActivity.getId());
}
if (ctx.magmaCore.get(ctx.startOfResearch.getId()) == null) {
System.err.println("Cannot find startOfResearch object with IRI: " + ctx.startOfResearch.getId());
}
if (ctx.magmaCore.get(ctx.stateOfPerson.getId()) == null) {
System.err.println("Cannot find stateOfPerson object with IRI: " + ctx.stateOfPerson.getId());
}
}

/**
* A class to hold the entities created and referenced by the use case.
*
* <p>
* All fields are public for ease of access and to reduce clutter from
* adding getters and setters. Lombok could be used to generate them
* without adding clutter.
* </p>
*/
private static class Context {
public MagmaCoreService magmaCore;

// New entities to be created.
public Activity researchActivity;
public Person person;
public PointInTime startOfResearch;
public StateOfPerson stateOfPerson;

// Ref data items.
public ClassOfPerson researchersClass;
public KindOfActivity researchActivityKind;
public Role researchRole;
}

/**
* An IRI prefix for the example.
*/
private static final IriBase TEST_BASE = new IriBase("test", "http://example.com/test#");

/**
* A class name for collecting together persons who are reseaechers.
*/
private static final String RESEARCHERS_CLASS_ENTITY_NAME = "Researchers";

/**
* The name of a kind of activity.
*/
private static final String RESEARCH_ACTIVITY_KIND_ENTITY_NAME = "Research Activities";

/**
* The name of a role for participants of research activities.
*/
private static final String RESEARCHER_ROLE_ENTITY_NAME = "Researcher Role";

/**
* A function to create the MagmaCoreService. In this case it is an in-memory
* database for the example.
*/
private static final Function<Context, Context> createMagmaCoreService = ctx -> {
ctx.magmaCore = MagmaCoreServiceFactory.createWithJenaDatabase();
return ctx;
};

/**
* A function to persist the new entities in the database.
*/
private static final Function<Context, Context> creatEntities = ctx -> {
ctx.magmaCore.create(ctx.person);
ctx.magmaCore.create(ctx.researchActivity);
ctx.magmaCore.create(ctx.startOfResearch);
ctx.magmaCore.create(ctx.stateOfPerson);
return ctx;
};

/**
* A function to find the Reference Data required by this use case.
*/
private static final Function<Context, Context> findRefData = ctx -> {
ctx.researchActivityKind = ctx.magmaCore.findByEntityName(RESEARCH_ACTIVITY_KIND_ENTITY_NAME);
ctx.researchRole = ctx.magmaCore.findByEntityName(RESEARCHER_ROLE_ENTITY_NAME);
ctx.researchersClass = ctx.magmaCore.findByEntityName(RESEARCHERS_CLASS_ENTITY_NAME);
return ctx;
};

/**
* A function to create a Person.
*/
private static final Function<Context, Context> createPerson = ctx -> {

/*
* Create a Person.
*/
ctx.person = SpatioTemporalExtentServices
.createPerson(randomIri());
ctx.person.addValue(HQDM.MEMBER_OF, ctx.researchersClass.getId());
return ctx;
};

/**
* A function to create an Activity.
*/
private static final Function<Context, Context> createResearchActivity = ctx -> {
/*
* Create a timestamp for use as the beginning of the axctivity.
*/
final String now = Instant.now().toString();

/*
* Create a PointInTime event.
*/
ctx.startOfResearch = SpatioTemporalExtentServices
.createPointInTime(randomIri());
ctx.startOfResearch.addValue(HQDM.VALUE_, now);

/*
* Create the Activity.
*/
ctx.researchActivity = SpatioTemporalExtentServices
.createActivity(randomIri());
ctx.researchActivity.addValue(HQDM.BEGINNING, ctx.startOfResearch.getId());
ctx.researchActivity.addValue(HQDM.MEMBER_OF_KIND, ctx.researchActivityKind.getId());
return ctx;
};

/**
* A function to add a state of person as a participant in the research activity.
*/
private static final Function<Context, Context> createPersonAsParticipantInActivity = ctx -> {
/*
* The state of person will be a participant in the research activity.
*/
ctx.stateOfPerson = SpatioTemporalExtentServices
.createStateOfPerson(randomIri());
ctx.stateOfPerson.addValue(HQDM.BEGINNING, ctx.startOfResearch.getId());
ctx.stateOfPerson.addValue(HQDM.MEMBER_OF_KIND, ctx.researchRole.getId());
ctx.stateOfPerson.addValue(HQDM.PARTICIPANT_IN, ctx.researchActivity.getId());
ctx.stateOfPerson.addValue(HQDM.TEMPORAL_PART_OF, ctx.person.getId());
ctx.stateOfPerson.addValue(RDFS.RDF_TYPE, HQDM.PARTICIPANT);

return ctx;
};

/**
* A utility function to generate random IRI values.
*/
private static final IRI randomIri() {
return new IRI(TEST_BASE, UUID.randomUUID().toString());
}

/**
* A function to populate Reference Data for the example.
*/
private static Function<Context, Context> populateRefData = ctx -> {

/*
* Create a Class of Person.
*/
final ClassOfPerson cop = ClassServices.createClassOfPerson(randomIri());
cop.addValue(HQDM.ENTITY_NAME, RESEARCHERS_CLASS_ENTITY_NAME);
ctx.magmaCore.create(cop);

/*
* Create a Kind of Activity.
*/
final KindOfActivity koa = ClassServices.createKindOfActivity(randomIri());
koa.addValue(HQDM.ENTITY_NAME, RESEARCH_ACTIVITY_KIND_ENTITY_NAME);
ctx.magmaCore.create(koa);

/*
* Create a Role.
*/
final Role role = ClassServices.createRole(randomIri());
role.addValue(HQDM.ENTITY_NAME, RESEARCHER_ROLE_ENTITY_NAME);
ctx.magmaCore.create(role);

return ctx;
};

/**
* A function to begin a read transaction.
*/
private static final Function<Context, Context> beginReadTransaction = magmaCore -> {
magmaCore.magmaCore.beginWrite();
return magmaCore;
};

/**
* A function to begin a write transaction.
*/
private static final Function<Context, Context> beginWriteTransaction = magmaCore -> {
magmaCore.magmaCore.beginWrite();
return magmaCore;
};

/**
* A function to commit a transaction.
*/
private static final Function<Context, Context> commitTransaction = magmaCore -> {
magmaCore.magmaCore.commit();
return magmaCore;
};

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package uk.gov.gchq.magmacore.examples.functional;

import org.junit.Test;

/**
* Execute the Functional Programming example for using MagmaCore.
*/
public class FunctionalProgrammingExampleTest {

/**
* A unit test to ensure that the FunctionalProgrammingExample code is executed as part of a build.
*/
@Test
public void test() {
FunctionalProgrammingExample.main(null);
}
}

0 comments on commit 5f038ee

Please sign in to comment.