FullCalendar JSON not populating calendar - arrays

I think I am missing something. I have set up Full Calendar, and have the default version working, but now am adding my own JSON, and it is not.
Code in the calendar page is
$(document).ready(function() {
$('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay,listWeek'
},
defaultDate: '2017-09-12',
editable: true,
navLinks: true, // can click day/week names to navigate views
eventLimit: true, // allow "more" link when too many events
events: {
url: 'php/get-events.php',
error: function() {
$('#script-warning').show();
}
},
loading: function(bool) {
$('#loading').toggle(bool);
}
});
});
I am learning how to encode JSON as I go along, and I found a tutorial online that gave me some code that seems to work. I have amended the original code in get-events.php to read like this (snippet with DB details taken out)...
// Require our Event class and datetime utilities
require dirname(__FILE__) . '/utils.php';
// Short-circuit if the client did not give us a date range.
if (!isset($_GET['start']) || !isset($_GET['end'])) {
die("Please provide a date range.");
}
// Parse the start/end parameters.
// These are assumed to be ISO8601 strings with no time nor timezone, like "2013-12-29".
// Since no timezone will be present, they will parsed as UTC.
$range_start = parseDateTime($_GET['start']);
$range_end = parseDateTime($_GET['end']);
// Parse the timezone parameter if it is present.
$timezone = null;
if (isset($_GET['timezone'])) {
$timezone = new DateTimeZone($_GET['timezone']);
}
class Emp {
public $id = "";
public $title = "";
public $start = "";
public $url = "";
}
while(!$JAN->atEnd()) {
e = new Emp();
$e->id = $JAN->getColumnVal("ID");
$e->title = $JAN->getColumnVal("TITLE");
$e->start = $JAN->getColumnVal("DATE")."T".$JAN->getColumnVal("TIME");
$e->url = "meeting_info.php?ID=".$JAN->getColumnVal("ID");
echo json_encode($e);
$JAN->moveNext();
}
$JAN->moveFirst(); //return RS to first record
// Read and parse our events JSON file into an array of event data arrays.
$json = file_get_contents(dirname(__FILE__) . '/../json/events.json');
$input_arrays = json_decode($json, true);
// Accumulate an output array of event data arrays.
$output_arrays = array();
foreach ($input_arrays as $array) {
// Convert the input array into a useful Event object
$event = new Event($array, $timezone);
// If the event is in-bounds, add it to the output
if ($event->isWithinDayRange($range_start, $range_end)) {
$output_arrays[] = $event->toArray();
}
}
// Send JSON to the client.
echo json_encode($output_arrays);
When I run the get-events.php page on it's own I get what I am assuming to be a correctly encoded JSON returned, one example in the array is ...
{"id":20,"title":"Executive Committee Meeting","start":"2017-05-01T00:00:00","url":"meeting_info.php?ID=20"}
Can anybody tell me what I have done wrong?

You need to run json_encode() on a complete array of PHP objects, not on each one individually. In your loop, add each Emp to an array, and then encode the array, when the loop ends.
If you look in your browser's network tab at the result of your ajax request, I think you're very likely to see a string of individual objects, but not wrapped in array (square) brackets, and not separated by commas, meaning the JSON is invalid. There's also a good chance there's an error message in your browser's console about the invalid data format. It's best to check this rather than assuming your JSON is correct. There are also online JSON validator tools you can paste it into, to validate the JSON in isolation.
Something like this should work better:
$events = array();
while(!$JAN->atEnd()) {
e = new Emp();
$e->id = $JAN->getColumnVal("ID");
$e->title = $JAN->getColumnVal("TITLE");
$e->start = $JAN->getColumnVal("DATE")."T".$JAN->getColumnVal("TIME");
$e->url = "meeting_info.php?ID=".$JAN->getColumnVal("ID");
$events[] = $e; //add event to the array
$JAN->moveNext();
}
echo json_encode($events); //encode the whole array as a coherent piece of JSON
//P.S. no need to run moveFirst really, since the request is going to end, and discard the resultset anyhow. Depending on your data access technique, you possibly need to close the recordset though, to avoid locking etc.
What you need your code to generate (and what fullCalendar is expecting), is a JSON array - here's a simple example containing 2 elements (representing events):
[
{ "id":20, "title":"Executive Committee Meeting", "start":"2017-05-01T00:00:00", "url":"meeting_info.php?ID=20" },
{ "id":21, "title":"Another Boring Committee Meeting", "start":"2017-05-02T00:00:00", "url":"meeting_info.php?ID=21" }
]
The example code I've given above should generate an array which is in the same format as this JSON sample.

Related

'504 - Gateway Timeout' when Indexing the items in Episerver Find

When Indexing the items, it fails sometimes and it gives,
The remote server returned an error: (504) Gateway Timeout. [The remote server returned an error: (504) Gateway Timeout.]
The Indexing logic is here as below,
var client = EPiServer.Find.Framework.SearchClient.Instance;
List<ItemModel> items = getItems(); // Get more than 1000 items
List<ItemModel> tempItems = new List<ItemModel>();
//Index 50 items at a time
foreach(var item in items)
{
tempItems.Add(item);
if (tempItems.Count == 50)
{
client.Index(tempItems);
tempItems.Clear();
}
}
What causes this to happen ?
Note: The above mentioned ItemModel is a custom model which is not implemented interfaces (such as IContent). And the items is a list of ItemModel objects.
Additional info:
EPiServer.Find.Framework version 13.0.1
EPiServer.CMS.Core version 11.9.2
I always figured the SearchClient to be a bit sketchy when manipulating data in Find, as far as I figured (but I have to check this) the SearchClient obey under the request limitation of Episerver Find and when doing bigger operations in loops it tends to time out.
Instead, use the ContentIndexer, i.e.
// Use this or injected parameter
var loader = ServiceLocator.Current.GetInstance<IContentLoader>();
// Remove all children or not
var cascade = true;
ContentReference entryPoint = ...where you want to start
// Get all indexable languages from Find
Languages languages = SearchClient.Instance.Settings.Languages;
// Remove all current instances of all languages below the selected content node
//languages.ForEach(x => ContentIndexer.Instance.RemoveFromIndex(entryPoint, cascade.Checked, x.FieldSuffix));
foreach (var lang in languages)
{
if (cascade)
{
var descendents = loader.GetDescendents(entryPoint);
foreach (ContentReference descendent in descendents)
{
ContentIndexer.Instance.RemoveFromIndex(descendent, false, lang.FieldSuffix);
}
}
// Try delete the entrypoint
var entryTest = loader.Get<IContent>(entryPoint, new CultureInfo(lang.FieldSuffix));
if (entryTest != null)
{
var delRes = ContentIndexer.Instance.Delete(entryTest);
}
}
This is the most bulletproof way to delete stuff from the index as far as I figured.

Insert multiple records into database with Vapor3

I want to be able to bulk add records to a nosql database in Vapor 3.
This is my Struct.
struct Country: Content {
let countryName: String
let timezone: String
let defaultPickupLocation: String
}
So I'm trying to pass an array of JSON objects but I'm not sure how to structure the route nor how to access the array to decode each one.
I have tried this route:
let countryGroup = router.grouped("api/country")
countryGroup.post([Country.self], at:"bulk", use: bulkAddCountries)
with this function:
func bulkAddCountries(req: Request, countries:[Country]) throws -> Future<String> {
for country in countries{
return try req.content.decode(Country.self).map(to: String.self) { countries in
//creates a JSON encoder to encode the JSON data
let encoder = JSONEncoder()
let countryData:Data
do{
countryData = try encoder.encode(country) // encode the data
} catch {
return "Error. Data in the wrong format."
}
// code to save data
}
}
}
So how do I structure both the Route and the function to get access to each country?
I'm not sure which NoSQL database you plan on using, but the current beta versions of MongoKitten 5 and Meow 2.0 make this pretty easy.
Please note how we didn't write documentation for these two libraries yet as we pushed to a stable API first. The following code is roughly what you need with MongoKitten 5:
// Register MongoKitten to Vapor's Services
services.register(Future<MongoKitten.Database>.self) { container in
return try MongoKitten.Database.connect(settings: ConnectionSettings("mongodb://localhost/my-db"), on: container.eventLoop)
}
// Globally, add this so that the above code can register MongoKitten to Vapor's Services
extension Future: Service where T == MongoKitten.Database {}
// An adaptation of your function
func bulkAddCountries(req: Request, countries:[Country]) throws -> Future<Response> {
// Get a handle to MongoDB
let database = req.make(Future<MongoKitten.Database>.self)
// Make a `Document` for each Country
let documents = try countries.map { country in
return try BSONEncoder().encode(country)
}
// Insert the countries to the "countries" MongoDB collection
return database["countries"].insert(documents: documents).map { success in
return // Return a successful response
}
}
I had a similar need and want to share my solution for bulk processing in Vapor 3. I’d love to have another experienced developer help refine my solution.
I’m going to try my best to explain what I did. And I’m probably wrong.
First, nothing special in the router. Here, I’m handling a POST to items/batch for a JSON array of Items.
router.post("items", "batch", use: itemsController.handleBatch)
Then the controller’s handler.
func createBatch(_ req: Request) throws -> Future<HTTPStatus> {
// Decode request to [Item]
return try req.content.decode([Item].self)
// flatMap will collapse Future<Future<HTTPStatus>> to [Future<HTTPStatus>]
.flatMap(to: HTTPStatus.self) { items in
// Handle each item as 'itm'. Transforming itm to Future<HTTPStatus>
return items.map { itm -> Future<HTTPStatus> in
// Process itm. Here, I save, producing a Future<Item> called savedItem
let savedItem = itm.save(on: req)
// transform the Future<Item> to Future<HTTPStatus>
return savedItem.transform(to: HTTPStatus.ok)
}
// flatten() : “Flattens an array of futures into a future with an array of results”
// e.g. [Future<HTTPStatus>] -> Future<[HTTPStatus]>
.flatten(on: req)
// transform() : Maps the current future to contain the new type. Errors are carried over, successful (expected) results are transformed into the given instance.
// e.g. Future<[.ok]> -> Future<.ok>
.transform(to: HTTPStatus.ok)
}
}

getting data from an object's array with vue.js

I am trying to access the following data in Vue.js
{"id":1,"name":"Westbrook","created_at":"2017-06-10T16:03:07.336Z","updated_at":"2017-06-10T16:03:07.336Z","stats":[{"id":1,"player_id":1,"points":2558,"assists":840,"rebounds":864,"created_at":"2017-06-10T16:03:07.373Z","updated_at":"2017-06-10T16:03:07.373Z"}]}
self.player = response.name works. now i need self.point
methods: {
fetchData: function() {
var self = this;
$.get("api/v1/players/1", function(response) {
console.log(response);
self.player = response.name;
self.point = response.stats.points
});
}
}
I have thus far tried response.stats["points"], response.stats[2], response.stats[ { points } ], response.stats[points]
The stats property in your json holds an array in which the first object has a property named points
So use response.stats[0].points
Keep in mind though that the stats is probably an array for a reason. It might hold more than one objects in the future, so always using the first element [0] might not be a valid approach.
I think it can help you
var json = JSON.parse(data);
json.stats[0].points
response = {"id":1,"name":"Westbrook","created_at":"2017-06-10T16:03:07.336Z","updated_at":"2017-06-10T16:03:07.336Z","stats":[{"id":1,"player_id":1,"points":2558,"assists":840,"rebounds":864,"created_at":"2017-06-10T16:03:07.373Z","updated_at":"2017-06-10T16:03:07.373Z"}]}
If you want to access the name
console.log(response.name) // Westbrook
If you want to access the stats data which contain list, simply target
let stats=response.stats[0] //By getting the first index in the list
Get the points in stats
console.log(stats.points) // 2588

angular JS select first instance of this item

So my question is: how do I scan the JSON in angular to find the first instance of isPrimary:true and then launch a function with the GUID that is in that item.
I have a webservice whos JSON defines available Accounts with a display name and a GUID this generates a dropdown select list that calls a function with the GUID included to return full data from a web service.
In the scenario where theres only 1 OPTION I dont show the SELECT and simply call the function with the single GUID to return the data from the service. If theres no options I dont show anything other than a message.
Code below shows what I currently have.
The Spec has now changed and the data they are sending me in the first service call which defines that select list is now including a property isPrimary:true on one of the JSON object along with its GUID as per the rest
I now need to change my interface to no longer use the SELECT list and instead fire the function call to the service for the item that contains the isPrimary:true property. However there may be multiple instances where isPrimary:true exists in the returning JSON so I just want to fire the function on the first found instance of isPrimary:true
Equally if that property isnt in any of the JSON items then just fire the function on the first item in the JSON.
My current Code is below - you can see the call to retrieve the full details is from function:
vm.retrieveAccount(GUID);
Where the GUID is supplied with each JSON object
Code is:
if (data.Accounts.length > 1) {
vm.hideAcc = false;
setBusyState(false);
//wait for the user to make a selection
} else if (data.Accounts.length == 1){
vm.hideAcc = true;
// Only 1 acc - no need for drop down get first item
vm.accSelected = data.Accounts[0].UniqueIdentifier;
vm.retrieveAccount(vm.accSelected);
} else {
// Theres no accounts
// Hide Drop down and show message
setBusyState(false);
vm.hideAcc = true;
setMessageState(false, true, "There are no Accounts")
}
Sample of new JSON structure
accName: "My Acc",
isPrimary: true,
GUID: "bg111010101"
Still think that's a weird spec, but simple enough to solve. Just step through the array and return the first isPrimary match. If none are found, return the first element of the array.
var findPrimary = function(data) {
if (!(Array.isArray(data)) || data.length == 0) {
return false; // not an array, or empty array
}
for (var i=0; i<data.length; i++) {
if (data[i].isPrimary) {
return data[i]; // first isPrimary match
}
}
// nothing had isPrimary, so return the first one:
return data[0];
}

Value is not an array ref error

I have a list of checkboxes. I am trying to pass the list of selected checkboxes to a perl script. I am obtaining the list of checkboxes using the folliwng code :
function exec(){
var checkedValue = "";
var inputElements = document.getElementsByTagName('input');
for(var i=0; inputElements[i]; i++){
if(inputElements[i].className==="chk" &&
inputElements[i].checked){
checkedValue += inputElements[i].value;
if (inputElements[i+1])
checkedValue += ", ";
else
checkedValue += "";
}
}
I am then passing "checkedValue" to a perl script as follows :
self.location='/cgi-bin/ATMRunJob.pl?tcs='+checkedValue;
In the perl script, I read the array as follows :
our #testCasesToRun = $var->param("tcs");
This is then assigned to a hash as follows :
my $runSpec = {
TestCasesToRun => #testCasesToRun
};
However, I get the following error when I load the page in the browser :
Failed TestLimits() with error: [hash: k=TestCasesToRun, v=1,]:[array]:Value is not an array ref
In check against following TLS:
[
'hr',
{
'OptDefaults' => {
'JobRunningGroupName' => 'astbluetooth',
'RunMode' => 'Queue',
'CountTowardsReporting' => 1,
'JobOwnerGroupName' => 'astbluetooth',
'SelectSetupTeardown' => 1
},
'Optional' => {
'TestCasesToRun' => [
'ar',
undef,
undef,
[
'r',
1,
undef
]
],
I am new to perl as well as CGI scripting. How could I get around this error?
NOTE : All the code snippets have been shortened for brevity, but still portray the essence of the problem.
EDIT : What I want to do is this. The user selects a list of test cases from a checkboxed list that he wants to execute. I take the test case ids of all the selected test cases and pass it to a perl script. In the perl script, I just need to assign these selected testcase ids to the TestCasesToRun element in the runspec hash.
What would be the correct way to do that?
You are assigning an array as a hashkey value. That doesn't work; you need to assign an array ref:
my $runSpec = {
TestCasesToRun => \#testCasesToRun
};
Given that the code compiles, I have a feeling you just messed up your examples in the Q - please fix them to accurately reflect your code, even if they will be slightly less brief.
Your 'tcs' parameter is a single string (assigned via JS). Why are you then assigning results of param('tcs') to an array in the first place? Do you have a split somewhere in your code that you didn't include into the example?
Your dump contains an array reference within an array reference. You need to elaborate on what the expected structure of TestCasesToRun arrayref is, and show the code which processes it in the test runner.
As per your last comment:
Change your JavaScript code to join using simple comma: checkedValue += ",";
Change your Perl assignment to: our #testCasesToRun = split(/,/, $var->param("tcs"));

Resources