While parsing input as JSON and persisting, only firsts JSON object is persisted - google-cloud-dataflow

I am trying to read array of JSON posted to a topic that my pipeline is subscribed to and persist the same to BigQuery. The problem I face while doing so is that it persists only the first object, can someone please provide me insight on what I am doing wrong here.
/** A DoFn that converts a table row from JSON into a BigQuery table row. */
static class FormatAsTableRowFn extends DoFn<TableRow, TableRow> {
private static final long serialVersionUID = 0;
static TableSchema getSchema() {
return new TableSchema().setFields(new ArrayList<TableFieldSchema>() {
// Compose the list of TableFieldSchema from tableSchema.
{
add(new TableFieldSchema().setName("PillBoxID").setType("STRING").setMode("NULLABLE"));
add(new TableFieldSchema().setName("Period").setType("STRING").setMode("NULLABLE"));
add(new TableFieldSchema().setName("Time").setType("TIMESTAMP").setMode("NULLABLE"));
add(new TableFieldSchema().setName("IsTaken").setType("STRING").setMode("NULLABLE"));
}
});
}
#Override
public void processElement(ProcessContext c) {
TableRow jsonRow = c.element();
// Setup a date formatter to parse the date appropriately
SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
try {
TableRow bQueryRow = new TableRow()
.set("PillBoxID", (String) jsonRow.get("PillBoxID"))
.set("Period", (String) jsonRow.get("Period"))
.set("Time",ft.format(ft.parse((String) jsonRow.get("Time"))))
.set("IsTaken", (String) jsonRow.get("IsTaken"));
LOG.error("Inside try" + bQueryRow.getF());
c.output(bQueryRow);
} catch (ParseException pe) {
LOG.error("ParseException");
LOG.error(pe.getMessage());
}
}
}
and my pipleline code is as shown below,
bigQueryPipeLine
.apply(PubsubIO.Read.topic(options.getPubsubTopic()).withCoder(TableRowJsonCoder.of()))
.apply(ParDo.of(new FormatAsTableRowFn()))
.apply(BigQueryIO.Write.to(tableSpec)
.withSchema(FormatAsTableRowFn.getSchema()));

it is possible to process multiple records if you format the input JSON to include an array of items.
Example input:
{
"items":
[
{"PillBoxID":"ID5", "Period":"Morning", "Time":"2016-03-14T11:11:11", "IsTaken":"true"},
{"PillBoxID":"ID6", "Period":"Afternoon", "Time":"2016-03-14T15:11:11", "IsTaken":"false"}
]
}
The rough example processElement() code adds the 2 items to c.output() for later storage in BigQuery.
#Override
public void processElement(ProcessContext c) throws DatastoreException, IOException{
TableRow jsonRowObj = c.element();
LOG.info("Original input:" + c.element().toPrettyString());
ArrayList<Map> jsonRows = (ArrayList<Map>)jsonRowObj.get("items");
Iterator<Map> iterator = jsonRows.iterator();
while(iterator.hasNext()) {
Map jsonRow = (Map)iterator.next();
// Setup a date formatter to parse the date appropriately
SimpleDateFormat ft = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
try {
LOG.info("child input JSON: "+jsonRow.toString());
TableRow bQueryRow = new TableRow()
.set("PillboxID", (String) jsonRow.get("PillBoxID"))
.set("Period", (String) jsonRow.get("Period"))
.set("Time",ft.format(ft.parse((String) jsonRow.get("Time"))))
.set("IsTaken", (boolean) Boolean.parseBoolean((String)jsonRow.get("IsTaken")));
c.output(bQueryRow);
} catch (ParseException pe) {
LOG.error("");
LOG.error("ParseException " +pe.getMessage());
}
}
}

Related

java.lang.IllegalArgumentException: calling sideInput() with unknown view

I tried to move the data from one table to another table. Used SideInput for filtering the records while transform the data. SideInput also type of KV collection and its loaded the data from another table.
When run my pipeline got "java.lang.IllegalArgumentException: calling sideInput() with unknown view" error.
Here is the entire code that I tried:
{
PipelineOptionsFactory.register(OptionPipeline.class);
OptionPipeline options = PipelineOptionsFactory.fromArgs(args).withValidation().as(OptionPipeline.class);
Pipeline p = Pipeline.create(options);
PCollection<TableRow> sideInputData = p.apply("ReadSideInput",BigQueryIO.readTableRows().from(options.getOrgRegionMapping()));
PCollection<KV<String,String>> sideInputMap = sideInputData.apply(ParDo.of(new getSideInputDataFn()));
final PCollectionView<Map<String,String>> sideInputView = sideInputMap.apply(View.<String,String>asMap());
PCollection<TableRow> orgMaster = p.apply("ReadOrganization",BigQueryIO.readTableRows().from(options.getOrgCodeMaster()));
PCollection<TableRow> orgCode = orgMaster.apply(ParDo.of(new gnGetOrgMaster()));
#SuppressWarnings("serial")
PCollection<TableRow> finalResultCollection = orgCode.apply("Process", ParDo.of(new DoFn<TableRow, TableRow>()
{
#ProcessElement
public void processElement(ProcessContext c) {
TableRow outputRow = new TableRow();
TableRow orgCodeRow = c.element();
String orgCodefromMaster = (String) orgCodeRow.get("orgCode");
String region = c.sideInput(sideInputView).get(orgCodefromMaster);
outputRow.set("orgCode", orgCodefromMaster);
outputRow.set("orgName", orgCodeRow.get("orgName"));
outputRow.set("orgName", region);
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS");
Date dateobj = new Date();
outputRow.set("updatedDate",df.format(dateobj));
c.output(outputRow);
}
}));
finalResultCollection.apply(BigQueryIO.writeTableRows()
.withSchema(schema)
.to(options.getOrgCodeTable())
.withCreateDisposition(BigQueryIO.Write.CreateDisposition.CREATE_IF_NEEDED)
.withWriteDisposition(BigQueryIO.Write.WriteDisposition.WRITE_APPEND));
p.run().waitUntilFinish();
}
#SuppressWarnings("serial")
static class getSideInputDataFn extends DoFn<TableRow,KV<String, String>>
{
#ProcessElement
public void processElement(ProcessContext c)
{
TableRow row = c.element();
c.output(KV.of((String) row.get("orgcode"), (String) row.get("region")));
}
}
It looks like the runner is complaining because you never told it about the side input when defining the graph. In this case you call .withSideInputs after the ParDo.of call passing in the reference to the PCollectionView<T> you defined earlier.
#SuppressWarnings("serial")
PCollection<TableRow> finalResultCollection = orgCode.apply("Process", ParDo.of(new DoFn<TableRow, TableRow>()
{
#ProcessElement
public void processElement(ProcessContext c) {
TableRow outputRow = new TableRow();
TableRow orgCodeRow = c.element();
String orgCodefromMaster = (String) orgCodeRow.get("orgCode");
String region = c.sideInput(sideInputView).get(orgCodefromMaster);
outputRow.set("orgCode", orgCodefromMaster);
outputRow.set("orgName", orgCodeRow.get("orgName"));
outputRow.set("orgName", region);
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS");
Date dateobj = new Date();
outputRow.set("updatedDate",df.format(dateobj));
c.output(outputRow);
}
}).withSideInputs(sideInputView));
I didn't test this code but that's what stands out when I look at it.

Dataflow DynamicDestinations unable to serialize org.apache.beam.sdk.io.gcp.bigquery.PrepareWrite

I am trying to use DynamicDestinations to write to a partitioned table in BigQuery where the partition name is mytable$yyyyMMdd. If I bypass dynamicdestinations and supply a hardcoded table name in .to(), it works; however, with dynamicdestinations I get the following exception:
java.lang.IllegalArgumentException: unable to serialize org.apache.beam.sdk.io.gcp.bigquery.PrepareWrite$1#6fff253c
at org.apache.beam.sdk.util.SerializableUtils.serializeToByteArray(SerializableUtils.java:53)
at org.apache.beam.sdk.util.SerializableUtils.clone(SerializableUtils.java:90)
at org.apache.beam.sdk.transforms.ParDo$SingleOutput.<init>(ParDo.java:591)
at org.apache.beam.sdk.transforms.ParDo.of(ParDo.java:435)
at org.apache.beam.sdk.io.gcp.bigquery.PrepareWrite.expand(PrepareWrite.java:51)
at org.apache.beam.sdk.io.gcp.bigquery.PrepareWrite.expand(PrepareWrite.java:36)
at org.apache.beam.sdk.Pipeline.applyInternal(Pipeline.java:514)
at org.apache.beam.sdk.Pipeline.applyTransform(Pipeline.java:473)
at org.apache.beam.sdk.values.PCollection.apply(PCollection.java:297)
at org.apache.beam.sdk.io.gcp.bigquery.BigQueryIO$Write.expandTyped(BigQueryIO.java:987)
at org.apache.beam.sdk.io.gcp.bigquery.BigQueryIO$Write.expand(BigQueryIO.java:972)
at org.apache.beam.sdk.io.gcp.bigquery.BigQueryIO$Write.expand(BigQueryIO.java:659)
at org.apache.beam.sdk.Pipeline.applyInternal(Pipeline.java:514)
at org.apache.beam.sdk.Pipeline.applyTransform(Pipeline.java:454)
at org.apache.beam.sdk.values.PCollection.apply(PCollection.java:284)
at com.homedepot.payments.monitoring.eventprocessor.MetricsAggregator.main(MetricsAggregator.java:82)
Caused by: java.io.NotSerializableException: com.google.api.services.bigquery.model.TableReference
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
And here is the code:
PCollection<Event> rawEvents = pipeline
.apply("ReadFromPubSub",
PubsubIO.readProtos(EventOuterClass.Event.class)
.fromSubscription(OPTIONS.getSubscription())
)
.apply("Parse", ParDo.of(new ParseFn()))
.apply("ExtractAttributes", ParDo.of(new ExtractAttributesFn()));
EventTable table = new EventTable(OPTIONS.getProjectId(), OPTIONS.getMetricsDatasetId(), OPTIONS.getRawEventsTable());
rawEvents.apply(BigQueryIO.<Event>write()
.to(new DynamicDestinations<Event, String>() {
private static final long serialVersionUID = 1L;
#Override
public TableSchema getSchema(String destination) {
return table.schema();
}
#Override
public TableDestination getTable(String destination) {
return new TableDestination(table.reference(), null);
}
#Override
public String getDestination(ValueInSingleWindow<Event> element) {
String dayString = DateTimeFormat.forPattern("yyyyMMdd").withZone(DateTimeZone.UTC).toString();
return table.reference().getTableId() + "$" + dayString;
}
})
.withFormatFunction(new SerializableFunction<Event, TableRow>() {
public TableRow apply(Event event) {
TableRow row = new TableRow();
Event evnt = (Event) event;
row.set(EventTable.Field.VERSION.getName(), evnt.getVersion());
row.set(EventTable.Field.TIMESTAMP.getName(), evnt.getTimestamp() / 1000);
row.set(EventTable.Field.EVENT_TYPE_ID.getName(), evnt.getEventTypeId());
row.set(EventTable.Field.EVENT_ID.getName(), evnt.getId());
row.set(EventTable.Field.LOCATION.getName(), evnt.getLocation());
row.set(EventTable.Field.SERVICE.getName(), evnt.getService());
row.set(EventTable.Field.HOST.getName(), evnt.getHost());
row.set(EventTable.Field.BODY.getName(), evnt.getBody());
return row;
}
})
.withCreateDisposition(BigQueryIO.Write.CreateDisposition.CREATE_IF_NEEDED)
.withWriteDisposition(BigQueryIO.Write.WriteDisposition.WRITE_APPEND)
);
Any pointers in the correct direction would be greatly appreciated.
Thanks!
From inspecting the exception message and the code above, it seems that the EventTable field used within your anonymous DynamicDestinations class contains a TableReference field which is not serializable.
One workaround would be to convert the anonymous DynamicDestinations to a static inner class and define a constructor which stores only the serializable pieces of the EventTable needed to implement the interface.
For example:
private static class EventDestinations extends DynamicDestinations<Event, String> {
private final TableSchema schema;
private final TableDestination destination;
private final String tableId;
private EventDestinations(EventTable table) {
this.schema = table.schema();
this.destination = new TableDestination(table.reference(), null);
this.tableId = table.reference().getTableId();
}
// ..
}
Looks like you're trying to fill a specific partition based on the event. Why not use:
SerializableFunction<ValueInSingleWindow<Event>, TableDestination>?

Google Dataflow write multiple line in BigQuery

I have a simple flow which aim is to write two lines in one BigQuery Table.
I use a DynamicDestinations because after that I will write on mutliple Table, on that example it's the same table...
The problem is that I only have 1 line in my BigQuery table at the end.
It stacktrace I see the following error on the second insert
"
status: {
code: 6
message: "Already Exists: Job sampleprojet3:b9912b9b05794aec8f4292b2ae493612_eeb0082ade6f4a58a14753d1cc92ddbc_00001-0"
}
"
What does it means ?
Is it related to this limitation ?
https://github.com/GoogleCloudPlatform/DataflowJavaSDK/issues/550
How can I do the job ?
I use BeamSDK 2.0.0, I have try with 2.1.0 (same result)
The way I launch :
mvn compile exec:java -Dexec.mainClass=fr.gireve.dataflow.LogsFlowBug -Dexec.args="--runner=DataflowRunner --inputDir=gs://sampleprojet3.appspot.com/ --project=sampleprojet3 --stagingLocation=gs://dataflow-sampleprojet3/tmp" -Pdataflow-runner
Pipeline p = Pipeline.create(options);
final List<String> tableNameTableValue = Arrays.asList("table1:value1", "table1:value2", "table2:value1", "table2:value2");
p.apply(Create.of(tableNameTableValue)).setCoder(StringUtf8Coder.of())
.apply(BigQueryIO.<String>write()
.withCreateDisposition(BigQueryIO.Write.CreateDisposition.CREATE_IF_NEEDED)
.withWriteDisposition(BigQueryIO.Write.WriteDisposition.WRITE_APPEND)
.to(new DynamicDestinations<String, KV<String, String>>() {
#Override
public KV<String, String> getDestination(ValueInSingleWindow<String> element) {
final String[] split = element.getValue().split(":");
return KV.of(split[0], split[1]) ;
}
#Override
public Coder<KV<String, String>> getDestinationCoder() {
return KvCoder.of(StringUtf8Coder.of(), StringUtf8Coder.of());
}
#Override
public TableDestination getTable(KV<String, String> row) {
String tableName = row.getKey();
String tableSpec = "sampleprojet3:testLoadJSON." + tableName;
return new TableDestination(tableSpec, "Table " + tableName);
}
#Override
public TableSchema getSchema(KV<String, String> row) {
List<TableFieldSchema> fields = new ArrayList<>();
fields.add(new TableFieldSchema().setName("myColumn").setType("STRING"));
TableSchema ts = new TableSchema();
ts.setFields(fields);
return ts;
}
})
.withFormatFunction(new SerializableFunction<String, TableRow>() {
public TableRow apply(String row) {
TableRow tr = new TableRow();
tr.set("myColumn", row);
return tr;
}
}));
p.run().waitUntilFinish();
Thanks
DynamicDestinations associates each element with a destination - i.e. where the element should go. Elements are routed to BigQuery tables according to their destinations: 1 destination = 1 BigQuery table with a schema: the destination should include just enough information to produce a TableDestination and a schema. Elements with the same destination go to the same table, elements with different destinations go to different tables.
Your code snippet uses DynamicDestinations with a destination type that contains both the element and the table, which is unnecessary, and of course, violates the constraint above: elements with a different destination end up going to the same table: e.g. KV("table1", "value1") and KV("table1", "value2") are different destinations but your getTable maps them to the same table table1.
You need to remove the element from your destination type. That will also lead to simpler code. As a side note, I think you don't need to override getDestinationCoder() - it can be inferred automatically.
Try this:
.to(new DynamicDestinations<String, String>() {
#Override
public String getDestination(ValueInSingleWindow<String> element) {
return element.getValue().split(":")[0];
}
#Override
public TableDestination getTable(String tableName) {
return new TableDestination(
"sampleprojet3:testLoadJSON." + tableName, "Table " + tableName);
}
#Override
public TableSchema getSchema(String tableName) {
List<TableFieldSchema> fields = Arrays.asList(
new TableFieldSchema().setName("myColumn").setType("STRING"));
return new TableSchema().setFields(fields);
}
})

Partial Unmarshalling of an XML using JAXB to skip some xmlElement

I want to unmarshal an XML file to java object using JAXB. The XML file is very large and contains some nodes which I want to skip in some cases to improve performance as these elements are non editable by client java program.
A sample XML is as follows:
<Example id="10" date="1970-01-01" version="1.0">
<Properties>...</Properties>
<Summary>...</Summary>
<RawData>
<Document id="1">...</Document>
<Document id="2">...</Document>
<Document id="3">...</Document>
------
------
</RawData>
<Location></Location>
<Title></Title>
----- // more elements
</Example>
I have two use cases:
unmarshal into Example object which contains Properties, Summaries, RawData etc. without skipping any RawData. (already done this part)
unmarshal into Example object which exclude RawData. Elements nested in RawData is very large so do not want to read this in this use case.
Now I want to unmarshal the XML such that RawData can be skipped. I have tried the technique provided at this link.
Using technique provided in above link also skips all elements which come after RawData.
I have fixed the issue with XMLEventReader with following code:
public class PartialXmlEventReader implements XMLEventReader {
private final XMLEventReader reader;
private final QName qName;
private boolean skip = false;
public PartialXmlEventReader(final XMLEventReader reader, final QName element) {
this.reader = reader;
this.qName = element;
}
#Override
public String getElementText() throws XMLStreamException {
return reader.getElementText();
}
#Override
public Object getProperty(final String name) throws IllegalArgumentException {
return reader.getProperty(name);
}
#Override
public boolean hasNext() {
return reader.hasNext();
}
#Override
public XMLEvent nextEvent() throws XMLStreamException {
while (isEof(reader.peek())) {
reader.nextEvent();
}
return reader.nextEvent();
}
#Override
public XMLEvent nextTag() throws XMLStreamException {
return reader.nextTag();
}
#Override
public XMLEvent peek() throws XMLStreamException {
return reader.peek();
}
#Override
public Object next() {
return reader.next();
}
#Override
public void remove() {
reader.remove();
}
#Override
public void close() throws XMLStreamException {
reader.close();
}
private boolean isEof(final XMLEvent e) {
boolean returnValue = skip;
switch (e.getEventType()) {
case XMLStreamConstants.START_ELEMENT:
final StartElement se = (StartElement) e;
if (se.getName().equals(qName)) {
skip = true;
returnValue = true;
}
break;
case XMLStreamConstants.END_ELEMENT:
final EndElement ee = (EndElement) e;
if (ee.getName().equals(qName)) {
skip = false;
}
break;
}
return returnValue;
}
}
While Unmarshalling just pass this eventReader to the unmarshal method
final JAXBContext context = JAXBContext.newInstance(classes);
final Unmarshaller um = context.createUnmarshaller();
Reader reader = null;
try {
reader = new BufferedReader(new FileReader(xmlFile));
final QName qName = new QName("RawData");
final XMLInputFactory xif = XMLInputFactory.newInstance();
final XMLEventReader xmlEventReader = xif.createXMLEventReader(reader);
final Example example =
(Example) um.unmarshal(new PartialXmlEventReader(xmlEventReader, qName));
}
} finally {
IOUtils.closeQuietly(reader);
}
I hope this would help
try {
// First create a new XMLInputFactory
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
// Setup a new eventReader
InputStream in = new FileInputStream("myXml");
XMLEventReader eventReader = inputFactory.createXMLEventReader(in);
// Read the XML document
Example example = null;
while (eventReader.hasNext()) {
XMLEvent event = eventReader.nextEvent();
if (event.isStartElement()) {
StartElement startElement = event.asStartElement();
// If we have a example element we create a new example
if (startElement.getName().getLocalPart().equals("Example")) {
example = new Example();
// We read the attributes from this tag and add the date
// and id attribute to our object
Iterator<Attribute> attributes = startElement
.getAttributes();
while (attributes.hasNext()) {
Attribute attribute = attributes.next();
if (attribute.getName().toString().equals("date")) {
example.setDate(attribute.getValue());
} else if (attribute.getName().toString().equals("id")) {
example.setId(attribute.getValue());
}
}
}
//get the Properties tag and add to object example
if (event.isStartElement()) {
if (event.asStartElement().getName().getLocalPart()
.equals("Properties")) {
event = eventReader.nextEvent();
example.setProperites(event.asCharacters().getData());
continue;
}
}
//get the Summary tag and add to object example
if (event.asStartElement().getName().getLocalPart()
.equals("Summary")) {
event = eventReader.nextEvent();
example.setSummary(event.asCharacters().getData());
continue;
}
// when you encounter the Rawdata tag just continue
//without adding it to the object created
if (event.asStartElement().getName().getLocalPart()
.equals("Rawdata")) {
event = eventReader.nextEvent();
// don't do anything
continue;
}
//get the location tag and add to object example
if (event.asStartElement().getName().getLocalPart()
.equals("Location")) {
event = eventReader.nextEvent();
example.setLocation(event.asCharacters().getData());
continue;
}
// read and add other elements that can be added
}
// If we reach the end of an example element/tag i.e closing tag
if (event.isEndElement()) {
EndElement endElement = event.asEndElement();
if (endElement.getName().getLocalPart().equals("Example")) {
//do something
}
}
}
} catch (FileNotFoundException | XMLStreamException e) {
}

generate different charts in the same activity using a spinner and achartEngine library

I am a novice in android and I am using achartEngine library to draw line Graph. what I want to do is to update the graph by using a spinner. For exmaple, if I chose the first option I will get a yellow graph and when
I swich to the second option in my spinner, the graph changes and becomes white with new title and data
for the moment I can only update the data but not the rest of the graph
package com.example.test2;
import java.text.Format;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import org.achartengine.ChartFactory;
import org.achartengine.GraphicalView;
import org.achartengine.chart.PointStyle;
import org.achartengine.model.SeriesSelection;
import org.achartengine.model.TimeSeries;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYSeriesRenderer;
import org.apache.http.NameValuePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.example.test2.R;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
public class Graphique extends Activity {
private GraphicalView mChart1;
private GraphicalView mChart2;
private TimeSeries glecimieSeries ;
private TimeSeries pressionSeries;
private XYMultipleSeriesDataset dataset;
private XYSeriesRenderer glecimieRenderer;
private XYSeriesRenderer pressionRenderer;
private XYMultipleSeriesRenderer multiRenderer;
// Progress Dialog
private ProgressDialog pDialog;
// Creating JSON Parser object
JSONParser jParser = new JSONParser();
ArrayList<HashMap<String, String>> listeappareil;
// url to get all products list
private static String url_liste_appareil ;
// JSON Node names
private static final String TAG_SUCCESS = "success";
private static String TAG_APPAREIL;
private static final String TAG_PID = "pid";
private static final String TAG_NOM = "nom";
private static final String TAG_J = "jour";
private static final String TAG_Y = "Y";
// products JSONArray
JSONArray appareil = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.graphique);
// getting intent data
Intent in = getIntent();
// Get JSON values from previous intent
String pid = in.getStringExtra(TAG_PID);
String nom = in.getStringExtra(TAG_NOM);
// Displaying all values on the screen
TextView lblNom = (TextView) findViewById(R.id.nom_patient);
lblNom.setText(nom);
Spinner mySpinner = (Spinner)findViewById(R.id.mesure);
ArrayAdapter adapter = ArrayAdapter.createFromResource(this, R.array.appareils, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mySpinner.setAdapter(adapter);
OnItemSelectedListener selectedGraph = new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> spinner, View container,
int position, long id) {
String graph = spinner.getItemAtPosition(position).toString().toLowerCase();
if(graph == "tensiometre"){
url_liste_appareil = "http://10.0.2.2/connexion_android/"+graph+".php";
TAG_APPAREIL = graph;
// Setting up chart
setupChart2();
// Start plotting chart
new ChartTask2().execute();
}else{
url_liste_appareil = "http://10.0.2.2/connexion_android/"+graph+".php";
TAG_APPAREIL = graph;
// Setting up chart
setupChart1();
// Start plotting chart
new ChartTask1().execute();
}
}
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
}
};
// Setting ItemClick Handler for Spinner Widget
mySpinner.setOnItemSelectedListener(selectedGraph);
}
private void setupChart1(){
// Creating TimeSeries for Glecimie
glecimieSeries = new TimeSeries("Glecimie");
// Creating a dataset to hold each series
dataset = new XYMultipleSeriesDataset();
// Adding Glecimie Series to the dataset
dataset.addSeries(glecimieSeries);
// Creating XYSeriesRenderer to customize glecimieSeries
glecimieRenderer = new XYSeriesRenderer();
glecimieRenderer.setColor(Color.YELLOW);
glecimieRenderer.setPointStyle(PointStyle.CIRCLE);
glecimieRenderer.setFillPoints(true);
glecimieRenderer.setLineWidth(2);
glecimieRenderer.setDisplayChartValues(true);
// Creating a XYMultipleSeriesRenderer to customize the whole chart
multiRenderer = new XYMultipleSeriesRenderer();
multiRenderer.setZoomButtonsVisible(true);
// Adding glecimieRenderer and pressionRenderer to multipleRenderer
// Note: The order of adding dataseries to dataset and renderers to multipleRenderer
// should be same
multiRenderer.addSeriesRenderer(glecimieRenderer);
// Getting a reference to LinearLayout of the MainActivity Layout
LinearLayout chartContainer = (LinearLayout) findViewById(R.id.chart_container);
// Creating a Time Chart
mChart1 = (GraphicalView) ChartFactory.getTimeChartView(getBaseContext(), dataset, multiRenderer,"dd-MMM-yyyy");
multiRenderer.setClickEnabled(true);
multiRenderer.setSelectableBuffer(10);
// Setting a click event listener for the graph
mChart1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Format formatter = new SimpleDateFormat("dd-MMM-yyyy");
SeriesSelection seriesSelection = mChart1.getCurrentSeriesAndPoint();
if (seriesSelection != null) {
int seriesIndex = seriesSelection.getSeriesIndex();
String selectedSeries="Glecimie";
if(seriesIndex==0)
selectedSeries = "Glecimie";
else
selectedSeries = "Pression";
// Getting the clicked Date ( x value )
long clickedDateSeconds = (long) seriesSelection.getXValue();
Date clickedDate = new Date(clickedDateSeconds);
String strDate = formatter.format(clickedDate);
// Getting the y value
int amount = (int) seriesSelection.getValue();
// Displaying Toast Message
Toast.makeText(
getBaseContext(),
"Prélèvement au " + strDate + " : " + amount ,
Toast.LENGTH_SHORT).show();
}
}
});
chartContainer.removeAllViews();
// Adding the Line Chart to the LinearLayout
chartContainer.addView(mChart1);
}
private class ChartTask1 extends AsyncTask<String, String, String>{
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(Graphique.this);
pDialog.setMessage("Loading data. Please wait...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(false);
pDialog.show();
}
// Generates dummy data in a non-ui thread
protected String doInBackground(String... params) {
// Building Parameters
List<NameValuePair> param = new ArrayList<NameValuePair>();
// getting JSON string from URL
JSONObject json = jParser.makeHttpRequest(url_liste_appareil, "GET", param);
// Check your log cat for JSON reponse
Log.d("Tous les appareil: ", json.toString());
try{
// Checking for SUCCESS TAG
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
// products found
// Getting Array of Products
appareil = json.getJSONArray(TAG_APPAREIL);
Date[] dt = new Date[appareil.length()];
// looping through All Products
for (int j = 0; j < appareil.length(); j++) {
JSONObject p = appareil.getJSONObject(j);
// Storing each json item in variable
GregorianCalendar gc = new GregorianCalendar(2012, 10, j+1);
dt[j] = gc.getTime();
String y = p.getString(TAG_Y);
String jr = p.getString(TAG_J);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d = sdf.parse(jr);
// Adding data to Glecimie and Pression Series
glecimieSeries.add(d,Double.parseDouble(y));
multiRenderer.setXTitle("Dates");
multiRenderer.setYTitle("Prélèvements");
}
}
}catch (JSONException e) {
e.printStackTrace();
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
// Plotting generated data in the graph
protected void onPostExecute(String file_url) {
// dismiss the dialog after getting all products
pDialog.dismiss();
// updating UI from Background Thread
runOnUiThread(new Runnable() {
public void run() {
/**
* Updating parsed JSON data into the graph
* */
mChart1.repaint();
}
});
}
}
private void setupChart2(){
// Creating TimeSeries for Pression
pressionSeries = new TimeSeries("Pression");
// Creating a dataset to hold each series
dataset = new XYMultipleSeriesDataset();
// Adding Glecimie Series to dataset
dataset.addSeries(pressionSeries);
// Creating XYSeriesRenderer to customize pressionSeries
pressionRenderer = new XYSeriesRenderer();
pressionRenderer.setColor(Color.YELLOW);
pressionRenderer.setPointStyle(PointStyle.CIRCLE);
pressionRenderer.setFillPoints(true);
pressionRenderer.setLineWidth(2);
pressionRenderer.setDisplayChartValues(true);
// Creating a XYMultipleSeriesRenderer to customize the whole chart
multiRenderer = new XYMultipleSeriesRenderer();
multiRenderer.setZoomButtonsVisible(true);
// Adding glecimieRenderer and pressionRenderer to multipleRenderer
// Note: The order of adding dataseries to dataset and renderers to multipleRenderer
// should be same
multiRenderer.addSeriesRenderer(pressionRenderer);
// Getting a reference to LinearLayout of the MainActivity Layout
LinearLayout chartContainer = (LinearLayout) findViewById(R.id.chart_container);
// Creating a Time Chart
mChart2 = (GraphicalView) ChartFactory.getTimeChartView(getBaseContext(), dataset, multiRenderer,"dd-MMM-yyyy");
multiRenderer.setClickEnabled(true);
multiRenderer.setSelectableBuffer(10);
// Setting a click event listener for the graph
mChart2.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Format formatter = new SimpleDateFormat("dd-MMM-yyyy");
SeriesSelection seriesSelection = mChart2.getCurrentSeriesAndPoint();
if (seriesSelection != null) {
int seriesIndex = seriesSelection.getSeriesIndex();
String selectedSeries="Glecimie";
if(seriesIndex==0)
selectedSeries = "Glecimie";
else
selectedSeries = "Pression";
// Getting the clicked Date ( x value )
long clickedDateSeconds = (long) seriesSelection.getXValue();
Date clickedDate = new Date(clickedDateSeconds);
String strDate = formatter.format(clickedDate);
// Getting the y value
int amount = (int) seriesSelection.getValue();
// Displaying Toast Message
Toast.makeText(
getBaseContext(),
" Prélèvement au" + strDate + " : " + amount ,
Toast.LENGTH_SHORT).show();
}
}
});
chartContainer.removeAllViews();
// Adding the Line Chart to the LinearLayout
chartContainer.addView(mChart2);
}
private class ChartTask2 extends AsyncTask<String, String, String>{
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(Graphique.this);
pDialog.setMessage("Loading data. Please wait...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(false);
pDialog.show();
}
// Generates dummy data in a non-ui thread
protected String doInBackground(String... params) {
// Building Parameters
List<NameValuePair> param = new ArrayList<NameValuePair>();
// getting JSON string from URL
JSONObject json = jParser.makeHttpRequest(url_liste_appareil, "GET", param);
// Check your log cat for JSON reponse
Log.d("Tous les appareil: ", json.toString());
try{
// Checking for SUCCESS TAG
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
// products found
// Getting Array of Products
appareil = json.getJSONArray(TAG_APPAREIL);
Date[] dt = new Date[appareil.length()];
// looping through All Products
for (int j = 0; j < appareil.length(); j++) {
JSONObject p = appareil.getJSONObject(j);
// Storing each json item in variable
GregorianCalendar gc = new GregorianCalendar(2012, 10, j+1);
dt[j] = gc.getTime();
String y = p.getString(TAG_Y);
String jr = p.getString(TAG_J);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d = sdf.parse(jr);
// Adding data to Glecimie and Pression Series
pressionSeries.add(d,Double.parseDouble(y));
multiRenderer.setChartTitle("la pression");
multiRenderer.setXTitle("Dates");
multiRenderer.setYTitle("Prélèvements");
}
}
}catch (JSONException e) {
e.printStackTrace();
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
// Plotting generated data in the graph
protected void onPostExecute(String file_url) {
// dismiss the dialog after getting all products
pDialog.dismiss();
// updating UI from Background Thread
runOnUiThread(new Runnable() {
public void run() {
/**
* Updating parsed JSON data into the graph
* */
mChart2.repaint();
}
});
}
}
// Initiating Menu XML file (menu.xml)
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_graphique, menu);
return (super.onCreateOptionsMenu(menu));
}
}
Please help
In order to refresh both charts, you need to call repaint on both:
mChart1.repaint();
mChart2.repaint();
You can update the color and other values of the chart by passing values.
In openChart() method you can pass the values.
private void openChart(final String[] code, final int[] distribution,final int color){
defaultRenderer.setBackgroundColor(color);
}

Resources