Set relationship between new object and existing ones - ios

I've got this problem with Core Data and I don't know where to go with it... Couldn't find any questions similiar to mine so here I am asking you this one:
I've got a Core Data Model where I have a couple of Entities but what's most important there is the central point entity called Activity and other entities are in relation with this one. One of those is Group entity. There is a many-to-many relation between Acitivity and Group tables.
Before I get any Activity from backend I need to select group for which Activities I want. This way I need to fetch and save to the database Groups before anything else - that's fine...
But after saving Groups I need to fetch Activities and each activity has many Groups (which are already in Core Data) and I need to estabilish connection between those.
I assume that I need to fetch all Groups and check them with those which came from Activity and after that I need to find it in Groups entity and add it to Activity?
I must tell you that the scenario I wrote above is far from logical and easy solution that's why I'm concerned about it. Any ideas on how to set this relation between new object and existing ones?
Detailed explanation
When user opens app it has an empty Core Data. So to fill it he has to select some groups and based on this selection application is able to send a request for timetable which consists of Activities. So, if groups are selected, user switches to the timetable view and timetable with activities is downloaded from API. Each activity has also groups in it but I want to use groups that I've already created rather than adding new ones to Core Data.
Take a look at some code examples:
First of all I make a request to API to get all groups and in response I get JSON:
[
{
"id": 507,
"name": "KrDZIs3011Io",
"category": 1,
"category_name": "grupa dziekańska",
"regular": true
},
"..."
]
So I handle this like:
for (id group in responseObject) {
Group *groupEntity = [NSEntityDescription insertNewObjectForEntityForName:#"Group" inManagedObjectContext:tempContext];
groupEntity.name = [group valueForKey:#"name"];
groupEntity.cashID = [group valueForKey:#"id"];
groupEntity.caseInsensitiveName = [[group valueForKey:#"name"] lowercaseString];
groupEntity.selected = #NO;
}
I'll skip the part where I'm checking for duplicates and saving MOC.
After this app is able to fetch groups which user selects (groupEntity.selected = #YES) and based on that create a request for timetable. In response user get something like:
{
"_id":"g552g3328",
"params":{
"group_id":[
552,
3328
]
},
"version":"5096146ca522d316c87b217b83a6c4d2",
"activities":[
{
"id":18282,
"places":[
{
"id":97,
"name":"Paw. C s. A",
"full_name":"Pawilon C sala A",
"regular":true
}
],
"tutors":[
{
"id":266,
"name":"prof. UEK Paweł Lula",
"short_name":"P. Lula",
"moodle_url":"https://e-uczelnia.uek.krakow.pl/course/view.php?id=1034",
"regular":true
}
],
"groups":[
{
"id":552,
"name":"KrDZIs3011Io",
"type":1,
"type_name":"Grupa dziekańska"
},
{
"id":553,
"name":"KrDZIs3011Si",
"type":1,
"type_name":"Grupa dziekańska"
},
{
"id":554,
"name":"KrDZIs3012Si",
"type":1,
"type_name":"Grupa dziekańska"
}
],
"category":1,
"category_name":"wykład",
"name":"Teoria grafów",
"regular_course":true,
"notes":"",
"url":"",
"date":"2014-02-07",
"day_of_week":5,
"day_of_week_text":"Pt",
"starts_at":"9:35",
"ends_at":"14:45",
"regular_schedule":true,
"starts_at_timestamp":1391765400,
"ends_at_timestamp":1391771100,
"unit_ordinal_number":"1",
"unit_total_count":"15",
"canceled":false,
"canceled_reason":""
},
...
]
}
As you can see I have an array of activities and each activity has an array of groups (which already are in Database but not linked).
So here I create new Entity for timetable and new entities for Activities and when I get to the Groups array in Activity how should I link it to the groups I already have in database?

Related

Quickbooks Online - Cannot get LinkedTxn detail

I created an Expense record and linked to an Invoice. When i import Invoice object through API, it has linked transaction as below.
"LinkedTxn":[{
"TxnId":"1356", //Id of Expense
"TxnType":"ReimburseCharge" //Type showing as ReimburseCharge
}]
In Quickbooks online docs, it is mentiond as
Links to expenses incurred on behalf of the customer are returned in
the response with LinkedTxn.TxnType set to ReimbCharge, ChargeCredit
or StatementCharge corresponding to billable customer expenses of type
Cash, Delayed Credit, and Delayed Charge, respectively. Links to these
types of transactions are established within the QuickBooks UI, only,
and are available as read-only at the API level.
Use LinkedTxn.TxnLineId as the ID in a separate read request for the
specific resource to retrieve details of the linked object.
In response it is showing TxnType as ReimburseCharge, but I didn't see any object like that in api explorer or docs. I don't know what type of object to request with id. I tried with Purchase, PurchaseOrder, Bill etc. but not of the request returned expected expense record.
Please help on how to access this Expense record with linked transaction id through api.
Invoice JSON:
Invoice line with description Printing paper is the expense linked in this invoice.
{
"Invoice":{
"Id":"1358",
"LinkedTxn":[
{
"TxnId":"1356",
"TxnType":"ReimburseCharge"
}
],
"Line":[
{
"Id":"1",
"LineNum":1,
"Description":"Printing paper",
"DetailType":"DescriptionOnly",
"DescriptionLineDetail":{
}
},
{
"Id":"3",
"LineNum":2,
"Description":"Magazine Monthly",
"Amount":100.0,
"DetailType":"SalesItemLineDetail",
"SalesItemLineDetail":{
"ItemRef":{
"value":"19",
"name":"Publications:Magazine Monthly"
},
"UnitPrice":100,
"Qty":1,
"TaxCodeRef":{
"value":"NON"
}
}
},
{
"Amount":250.0,
"DetailType":"SubTotalLineDetail",
"SubTotalLineDetail":{
}
}
],
"Balance":250.0
}
}
The docs are a little confusing on this point - unfortunately, the second paragraph
Use LinkedTxn.TxnLineId as the ID in a separate read request for the specific resource to retrieve details of the linked object.
is a generic paragraph that appears for docs on every Ref type, but shouldn't appear here. It's impossible to access these transactions via the API. More detail available here.

Strategy for mapping locally created core data objects to webserver and back

I'm trying to devise a solution to use across many models for mapping locally created nested relationships to the server and back. Consider that I have the following models:
Order
LineItem
Payment
An Order has many line items and payments.
If I create an order locally, send it to server, I can map the id attribute assigned by the server no problem, as I am working on the same object. However, my dilemma revolves around submitting nested attributes to the server. Consider this:
I create an Order and Payment locally, and send to the server's REST endpoint for Order
{"email":"test#gmail.com", payments_attributes: [{"amount": 15.50}, {"amount": 12}]}
And I receive a JSON response with something like:
{"id": 10, "email":"test#gmail.com", payments_attributes: [{"id": 50, "amount": 15.50}, {"id": 51, "amount": 12}}
What's a good strategy for mapping these nested payment id attributes back to the correct Core Data object? I can obviously check things like payment amount, but thats brittle (imagine the edge cases!), and works only for the payment model. I need a generalized solution to use for many different models. The only thing I can think of is to create a UUID locally, send it up, and have the server push it back along with the id, but that requires a lot of patching on the server side and I hate the idea of the client managing unique attributes. My backend is Rails and I want this working with minimal (if any) changes to the server side.
I've been previously using RestKit, and it magically worked (I think it nuked the local objects and created new ones) but I am working towards dropping that dependency and doing the mapping myself... for performance and control reasons.
It sounds like you already know what you need to do.
With the current server API, you can only match up data by looking at values. Since they're not guaranteed to be unique, the results of matching them are not guaranteed to be reliable.
If you send some unique ID to the server, which returns it, then you have a unique key that unambiguously matches your outgoing data to the new incoming data. The server doesn't need to store this ID or do anything else with it-- it just needs to make sure to take the incoming ID and include it with the outgoing result data. It doesn't even matter if multiple clients have the same unique IDs, since all they're doing with them is matching outgoing server data to returned data.
Here is the solution I've come up with.
Server Modifications
Add a Ruby Mixin to all ActiveRecord::Base classes that will utilize this
Create a reusable base template for rendering JSON
Mixin
module CoreDataIdentification
extend ActiveSupport::Concern
included do
attr_accessor :object_id
end
end
And to use it:
class LineItem < ActiveRecord::Base
include CoreDataIdentification
end
Or simply (in an initializer):
ActiveRecord::Base.send :include, CoreDataIdentification
Easy enough! This works well, because the ID is not persisted to the server database, it simply passes back whatever it was given.
JSON Template (using the RABL gem)
attributes :id
attributes :object_id, if: lambda {|object| object&.object_id }
node(:errors) { |object| object.errors }
node(:created_at) {|object| object.api_timestamp(:created_at) }
node(:deleted_at) {|object| object.api_timestamp(:deleted_at) }
node(:updated_at) {|object| object.api_timestamp(:updated_at) } if locals[:index]
node(:errors) { |object| object.errors } if locals[:errors]
Example order.rabl
object #line_item
extends('base', locals: {index: locals[:index], errors: locals[:errors]})
attributes :order_id, :price, :adjustment_total, :promo_total, :additional_tax_total, :included_tax_total, :additional_service_fee_total, :included_service_fee_total, :quantity, :variant_id, :currency, :pre_tax_amount
Client side code (Objective-C)
Subclass NSManagedObject to serve as a template for managing commonly shared attributes between all models (id, object_id, created_at, etc)
Method: Serialize core data attributes to JSON
Method: Map JSON attributes to Core Data
Method: Map JSON relationships to Core Data
Some sample code that I am still playing with:
- (NSMutableDictionary *)requestParams
{
NSMutableDictionary *params = [NSMutableDictionary dictionary];
if (self.id)
params[#"id"] = self.id;
else {
params[#"object_id"] = self.objectID.URIRepresentation.absoluteString;
}
return params;
}
And to map the relationships:
- (void)mapRelationships:(NSDictionary *)JSON
{
[self connectKeyPath:#"line_items" dict:JSON entity:#"LineItem"];
[self connectKeyPath:#"payments" dict:JSON entity:#"Payment"];
//[self connectKeyPath:#"adjustments" dict:JSON entity:#"Adjustment"];
}
+ (GMManagedObject *)objectFromURL:(NSString *)URL inContext:(NSManagedObjectContext *)context
{
NSURL *url = [NSURL URLWithString:URL];
NSManagedObjectID *objectID = [[GListManager shared].managedObjectStore.persistentStoreCoordinator managedObjectIDForURIRepresentation:url];
return [context objectWithID:objectID];
}
- (void)connectKeyPath:(NSString *)keyPath dict:(NSDictionary *)JSON entity:(NSString *)entityName
{
NSArray *items = [JSON objectForKey:keyPath];
for (NSDictionary *item_attributes in items) {
NSString *object_id = [item_attributes objectForKey:#"object_id"];
NSNumber *id = [item_attributes objectForKey:#"id"];
GMManagedObject *item;
if (object_id) {
// find local
item = [self.class objectFromURL:object_id inContext:self.managedObjectContext];
} else {
item = [self findOrCreateByEntity:entityName andId:id];
}
[item mapResponse:item_attributes];
}
}
I still have a lot of work to do in refactoring this for flexibility and performance... but I think this is a step in the right direction... rendering thousands of lines of RestKit code moot.

firebase session for two users [duplicate]

In my main page I have a list of users and i'd like to choose and open a channel to chat with one of them.
I am thinking if use the id is the best way and control an access of a channel like USERID1-USERID2.
But of course, user 2 can open the same channel too, so I'd like to find something more easy to control.
Please, if you want to help me, give me an example in javascript using a firebase url/array.
Thank you!
A common way to handle such 1:1 chat rooms is to generate the room URL based on the user ids. As you already mention, a problem with this is that either user can initiate the chat and in both cases they should end up in the same room.
You can solve this by ordering the user ids lexicographically in the compound key. For example with user names, instead of ids:
var user1 = "Frank"; // UID of user 1
var user2 = "Eusthace"; // UID of user 2
var roomName = 'chat_'+(user1<user2 ? user1+'_'+user2 : user2+'_'+user1);
console.log(user1+', '+user2+' => '+ roomName);
user1 = "Eusthace";
user2 = "Frank";
var roomName = 'chat_'+(user1<user2 ? user1+'_'+user2 : user2+'_'+user1);
console.log(user1+', '+user2+' => '+ roomName);
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>
A common follow-up questions seems to be how to show a list of chat rooms for the current user. The above code does not address that. As is common in NoSQL databases, you need to augment your data model to allow this use-case. If you want to show a list of chat rooms for the current user, you should model your data to allow that. The easiest way to do this is to add a list of chat rooms for each user to the data model:
"userChatrooms" : {
"Frank" : {
"Eusthace_Frank": true
},
"Eusthace" : {
"Eusthace_Frank": true
}
}
If you're worried about the length of the keys, you can consider using a hash codes of the combined UIDs instead of the full UIDs.
This last JSON structure above then also helps to secure access to the room, as you can write your security rules to only allow users access for whom the room is listed under their userChatrooms node:
{
"rules": {
"chatrooms": {
"$chatroomid": {
".read": "
root.child('userChatrooms').child(auth.uid).child(chatroomid).exists()
"
}
}
}
}
In a typical database schema each Channel / ChatGroup has its own node with unique $key (created by Firebase). It shouldn't matter which user opened the channel first but once the node (& corresponding $key) is created, you can just use that as channel id.
Hashing / MD5 strategy of course is other way to do it but then you also have to store that "route" info as well as $key on the same node - which is duplication IMO (unless Im missing something).
We decided on hashing users uid's, which means you can look up any existing conversation,if you know the other persons uid.
Each conversation also stores a list of the uids for their security rules, so even if you can guess the hash, you are protected.
Hashing with js-sha256 module worked for me with directions of Frank van Puffelen and Eduard.
import SHA256 from 'crypto-js/sha256'
let agentId = 312
let userId = 567
let chatHash = SHA256('agent:' + agentId + '_user:' + userId)

Get meetings by organizer or attendee email ID using GoToMeeting SDK

I am using .Net sdk of GoToMeeting.
I want to get meetings organized by particular organizer.
I have tried using
MeetingsApi.getHistoryMeetings but it does not return me OrganizerKey so I can not filter on particular Organizer.
Is there any way to get meeting(s) based on organizer or even by Attendee email ID by using .Net SDK?
What is the problem you are facing with MeetingsApi.getHistoryMeetings();?
why you need to filter the method, the MeetingsApi.getHistoryMeetings(accessToken,true,date1,date2); itself filtered for a particular user right?
Look on the arguments we are passing in the method?
accessToken - This token is generated as a result of successful authentication of a gotoproduct account. (In API call it can be generated using directlogin orOauth method.
true - this represents whether the meetings returned are past or not.
date1 - Start date range for the meetings.
date2 - End date range for the meetings.
below code is the sample for getting history meetings.
DateTime sdt=DateTime.Parse("07/01/2015");
DateTime edt=DateTime.Parse("07/30/2015");
List<MeetingHistory> historymeets = new System.Collections.Generic.List<MeetingHistory>();
historymeets=meeting.getHistoryMeetings(accesToken, true, sdt, edt);
foreach (var item in historymeets)
{
Console.WriteLine(item.subject);
}
try it out... The above code will store the meetings in historymeets collection object.
You can do the filter function in that collection object.
UPDATE :
List<MeetingHistory> historymeets = new System.Collections.Generic.List<MeetingHistory>();
historymeets=meeting.getHistoryMeetings(accesToken, true, sdt, edt);
List<AttendeeByMeeting> lstAttendee = new System.Collections.Generic.List<AttendeeByMeeting>();
foreach (var item in historymeets)
{
Console.WriteLine(item.meetingId);
lstAttendee=meeting.getAttendeesByMeetings(accesToken, item.meetingId);
foreach (var itemattendee in lstAttendee)
{
Console.WriteLine(itemattendee.attendeeEmail);
}
}
for comment - It is possible, but not directly because there is no api calls, which supports the meeting by attendee . the above code which i have written is for meeting by organizer . Now you have two options,
get the getHistoryMeetings, now you got the meeting details right? , then get the attendees by meeting id using getAttendeesByMeetings(), filter the two different collection objects with join using LINQ. OR
get the meetingdetails and attendees by executing two different fuinction calls, and store it in database or somewhere else, so that you can access it for doing the filter

Ember.js route for the current user's /settings page

A common pattern for a user's settings page would be for it to live at /settings.
In my Rails app, I'm accomplishing this on the API side by mapping get 'settings' to Settings#show and looking for the current_user's settings.
But on the Ember side, I'm stumped. There's no ID to use for the GET request, so I can't use the typical pattern of this.store.find('setting', params.id) within my route.
What's the "Ember way" of handling this sort of use case?
This has been discussed here: http://discuss.emberjs.com/t/fetching-single-records/529/3
The issue with loading a single record not based on an ID, is that you need to get back a DS.Model object as a promise. If you get back a record that's already in the client's memory you would now have two different objects representing the same record (type and id combination). Take this example:
var user123 = App.User.find(123);
var currentUser = App.findByUrl('/users/current'); //This is an imaginary method, i.e. Ember Data don't support it
notEqual(user123, currentUser, "The user objects can't be the same cause we don't know what the current user is yet");
Now we get this response from the server:
{
"user": {
"id": 123,
"name": "Mufasa"
}
}
Now currentUser and user123 both have id 123, but they are essentially different objects = very bad. This is why this approach wouldn't work.
Instead you will want to load a record array of users, listen for it to load, and then take the firstObject from the loaded records. Like this:
var users = App.User.find({ is_current: true });
users.one('didLoad', function() {
App.set('currentUser', users.get('firstObject');
});
$.ajax({
type: 'GET',
url: '/users/current',
success: function(payload) {
var store = this.store;
var userReference = store.load(App.User, payload.user);
App.set('currentUser', store.recordForReference(userReference));
}.bind(this)
});

Resources