Generating JSON-formatted data from Google Ads results - c#

The code below is what I'm currently using to generate a JSON representation of the captured data. My question is, is the use of the Google.Protobuf.Reflection.MessageDescriptor and the use of Reflection. The code appears to work. However, is there a better way to do this?
The code is called by a ClearScript-enabled JavaScript instance, thus the strings rather than longs in the parameters.
public string GenerateHistoricalMetrics(GoogleAdsClient client, string customerIdString, string locationIdsList,string languageIdString, string keywordTextsList, string pageUrl, bool debug = true)
{
if (debug) Debugger.Launch();
long[] locationIds = (from id in locationIdsList.Split(',') select long.Parse(id)).ToArray();
string[] keywordTexts = keywordTextsList.Split(',');
long customerId = long.Parse(customerIdString);
long languageId = long.Parse(languageIdString);
KeywordPlanIdeaServiceClient keywordPlanIdeaService =
client.GetService(Services.V10.KeywordPlanIdeaService);
// Make sure that keywords and/or page URL were specified. The request must have
// exactly one of urlSeed, keywordSeed, or keywordAndUrlSeed set.
if (keywordTexts.Length == 0 && string.IsNullOrEmpty(pageUrl))
{
return JsonConvert.SerializeObject(new JSON() { Error = "At least one of keywords or page URL is required, but neither was specified." });
}
// Specify the optional arguments of the request as a keywordSeed, UrlSeed,
// or KeywordAndUrlSeed.
GenerateKeywordIdeasRequest request = new GenerateKeywordIdeasRequest()
{
CustomerId = customerId.ToString(),
};
if (keywordTexts.Length == 0)
{
// Only page URL was specified, so use a UrlSeed.
request.UrlSeed = new UrlSeed()
{
Url = pageUrl
};
}
else if (string.IsNullOrEmpty(pageUrl))
{
// Only keywords were specified, so use a KeywordSeed.
request.KeywordSeed = new KeywordSeed();
request.KeywordSeed.Keywords.AddRange(keywordTexts);
}
else
{
// Both page URL and keywords were specified, so use a KeywordAndUrlSeed.
request.KeywordAndUrlSeed = new KeywordAndUrlSeed
{
Url = pageUrl
};
request.KeywordAndUrlSeed.Keywords.AddRange(keywordTexts);
}
// Create a list of geo target constants based on the resource name of specified
// location IDs.
foreach (long locationId in locationIds)
{
request.GeoTargetConstants.Add(ResourceNames.GeoTargetConstant(locationId));
}
request.Language = ResourceNames.LanguageConstant(languageId);
// Set the network. To restrict to only Google Search, change the parameter below to
// KeywordPlanNetwork.GoogleSearch.
request.KeywordPlanNetwork = KeywordPlanNetwork.GoogleSearch; //.GoogleSearchAndPartners;
var list = new List<Dictionary<string, object>>();
try
{
// Generate keyword ideas based on the specified parameters.
var response =
keywordPlanIdeaService.GenerateKeywordIdeas(request);
// Iterate over the results and print its detail.
foreach (GenerateKeywordIdeaResult result in response)
{
KeywordPlanHistoricalMetrics metrics = result.KeywordIdeaMetrics;
Google.Protobuf.Reflection.MessageDescriptor descriptor = GenerateKeywordIdeaResult.Descriptor;
foreach (var field in descriptor.Fields.InDeclarationOrder())
{
object value = field.Accessor.GetValue(result);
if (value != null)
{
var props = value.GetType().GetProperties(BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.GetField | BindingFlags.Instance);
var dict = new Dictionary<string, object>();
foreach (string key in from prop in props
let key = Title(field.JsonName) + "." + prop.Name
where key.StartsWith("Keyword")
select key
)
{
dict.Add(key, value);
}
if (dict.Count() > 0)
list.Add(dict);
}
}
}
}
catch (GoogleAdsException e)
{
return JsonConvert.SerializeObject(new JSON() { Error = $"Failure: Message: {e.Message}, Failure: {e.Failure}, Request ID: {e.RequestId}" });
}
return JsonConvert.SerializeObject(new JSON() { Cargo = list, Crew = resultList });
}

Related

Is there a built-in method to convert .NET Configuration to JSON?

It's easy to convert JSON into configuration, e.g. with
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
var configuration = new ConfigurationBuilder().AddJsonStream(stream).Build();
which gives you an IConfigurationRoot.
Is there a method (preferably in one of the Microsoft.Extensions.Configuration packages) that does the reverse?
Context: I'm downloading a bunch of Azure App Configuration settings and want to export them as JSON. Similar functionality is available in the Azure Portal but I want to resolve key vault references as well.
I can probably do something like this:
// Convert to JSON
var jRoot = new JObject();
foreach (var setting in settings) {
Add(jRoot, setting.Key, setting.Value);
}
with the Add method defined as
private void Add(JObject jObject, string key, string value) {
var index = key.IndexOf(':');
if (index == -1) {
jObject[key] = value;
return;
}
var prefix = key[..index];
if (!jObject.ContainsKey(prefix)) {
jObject[prefix] = new JObject();
}
Add((JObject)jObject[prefix], key[(index + 1)..], value);
}
which I'd probably need to extend to support arrays, but I was hoping I'd not have to reinvent the wheel.
For now, I've expanded the Add method to support arrays:
private void Add(JToken jToken, string key, string value) {
var components = key.Split(":", 3);
if (components.Length == 1) {
// Leaf node
if (jToken is JArray jArray_) {
jArray_.Add(value);
} else {
jToken[components[0]] = value;
}
return;
}
// Next level
JToken nextToken;
var nextTokenIsAnArray = int.TryParse(components[1], out _);
if (jToken is JArray jArray) {
var index = int.Parse(components[0]);
if (jArray.Count == index) {
nextToken = nextTokenIsAnArray ? new JArray() : (JToken)new JObject();
jArray.Add(nextToken);
} else {
nextToken = jArray[index];
}
} else {
nextToken = jToken[components[0]];
if (nextToken == null) {
nextToken = jToken[components[0]] = nextTokenIsAnArray ? new JArray() : (JToken)new JObject();
}
}
Add(nextToken, key[(components[0].Length + 1)..], value);
}
which works for me, assuming arrays appear in the right order, e.g.
key "Foo:0", value "Bar"
key "Foo:1", value "Baz"
which gets serialized as
{ "Foo": ["Bar", "Baz"] }

Store List of Objects into a column and trigger a Mail - C#, SQL Server

Trying to store list of users (objects) into the database and trigger a Mail to the recipient with the same list. I am using MSSQL and C# for my project. Below code is running successfully if I intend to save just one user. But I am having issues to save a list of users. I am requesting to help me with some ideas on how to achieve the following. Thank you.
public class ExportService{
public string SendImportStatus(List<Import> users)
{
Let<Notification>().SaveListToSendEmail(users);
return null;
}
}
public class Notification{
public void SaveListToSendEmail(List<Import> users)
{
if (IsNullOrEmpty(users))
{
return;
}
SendEmailBody(actedBy, users);
}
}
private void SendEmailBody(List<Import> users)
{
var getManagers = Let<User>().GetAll();
var type = NotificationType.ModuleImport;
var recipientEmails = getManagers .Select(model => model.Email);
var emailToList = string.Join(";", recipientEmails);
var template = Let<EmailTemplate>().GetItemByCode(EnumHelper.GetDescription(type));
var fileName = users.Select(x => x.FileName).First();
var notification = new NotificationM()
{
Type = type,
To = emailToList
};
notification.Value = new
{
ModuleCode = fileName,
list = users //[{"Id":"A","Status":"Success},{"Id":"B","Status":"Fail"},..]
};
var recipient = BuildRecipient(template, notification);
Let<Recipient>().Save(recipient, actedBy);
}
}
Building the Recipient with above data.
protected RecipientDM BuildRecipient(EmailTemplateDM template, NotificationM notificationM)
{
var subject = StringHelper.Fill(template.GetSubject(), notificationM.Value);
var body = StringHelper.Fill(template.GetContent(), notificationM.Value);
return BuildRecipient(notificationM.Id, notificationM.Type, recipient =>
{
recipient.To = notificationM.To;
recipient.Cc = notificationM.Cc;
recipient.Bcc = notificationM.Bcc;
recipient.Subject = subject;
recipient.Body = body;
});
}
String Helper Class, for now its able to store the value of ModuleCode into the database. But I am not able to figure out how to save the list.
public static string Fill(object value)
{
var pairs = ConvertHelper.ToPairs(value);
foreach (var key in pairs.Keys)
{
var text = pairs[key] == null ? string.Empty : pairs[key].ToString();
template = Regex.Replace(template, $"##{key}##".Trim(), text, RegexOptions.IgnoreCase);
}
return template;
}
Database column Body has this hard coded where ##ModuleCode## and ##list## will get replaced.
'<p>Dear Manager(s), <br /> <br />Module - ##ModuleCode## - is imported.<br /><br /><h3>Please find More Details below</h3><br />
<table><tr><td>##list##<td></tr></table>
I am expecting the following email result.
Dear Manager(s),
Module - abc is imported
Please find more details below.
---------------
| A | Success |
| B | Failure |
---------------

Quickbooks Online API - Payment 'Unapplied' when Invoice ID provided

I've managed to create a payment using the C# .NET SDK, however it keeps showing up as 'unapplied' when I check in QBO.
I am providing the Invoice ID and tried to follow their developer API documentation but I been at this so long now that maybe I am missing something?
The following code creates the payment but doesn't 'receive' the payment towards the invoice, is there something I missed or need to do in order for the two to be linked together?
Payment payment = new Payment
{
ProcessPayment = false,
CustomerRef = new ReferenceType { name = customer.DisplayName, Value = customer.Id },
CurrencyRef = new ReferenceType { type = "Currency", Value = "CAD" },
TotalAmt = amount,
TotalAmtSpecified = true
};
if (method == PaymentTypes.Cash)
{
var paymentType = paymentMethods.FirstOrDefault(o => o.Id == "1");
if (paymentType != null)
{
payment.PaymentMethodRef = new ReferenceType()
{name = paymentType.Name, Value = paymentType.Id};
}
}
if (method == PaymentTypes.DebitCard)
{
var paymentType = paymentMethods.FirstOrDefault(o => o.Id == "9");
if (paymentType != null)
{
payment.PaymentMethodRef = new ReferenceType()
{ name = paymentType.Name, Value = paymentType.Id };
}
}
if (method == PaymentTypes.CreditCard)
{
var paymentType = paymentMethods.FirstOrDefault(o => o.Id == "8");
if (paymentType != null)
{
payment.PaymentMethodRef = new ReferenceType()
{ name = paymentType.Name, Value = paymentType.Id };
}
}
List<LinkedTxn> linkedTxns = new List<LinkedTxn>
{
new LinkedTxn()
{
TxnId = invoice.Id,
TxnType = TxnTypeEnum.Invoice.ToString()
},
};
foreach (Line line in invoice.Line)
{
//line.Amount = amount;
//line.AmountSpecified = true;
line.Received = amount;
line.ReceivedSpecified = true;
line.DetailType = LineDetailTypeEnum.PaymentLineDetail;
line.DetailTypeSpecified = true;
line.LinkedTxn = linkedTxns.ToArray();
}
payment.DepositToAccountRef = new ReferenceType() { Value = "5" };
payment.Line = invoice.Line;
payment.PaymentRefNum = reference;
DataService dataService = new DataService(serviceContext);
dataService.Add<Payment>(payment);
This is not an answer. However there's too much to add to the comments. I'm hoping this will be a helpful starting point (if not I'll remove it later).
Firstly I'd suggest refactoring your code and parameterise your variables. Doing so you should be able to preform repeatable testing in isolation.
When you preform the dataService.Add<Payment>(payment), store the response object as it may offer clues on how the request was processed. Alternatively if this is suppressing error messages, you might want to try an use Postman to send HTTP requests. This may help determine what's parameters are missing/ incorrect.
Avoid creating objects that are partially assigned it makes it a lot easier to read 7 work out which properties need to be assigned.
Also if you have a look at Full update a payment section on https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/payment
the json payload has an additional Line with the property TxnType set to CreditMemo. I would assume you'll need to add something like ReceivePayment or CreditCardPayment?
To refactor your code consider:
// TODO - Set variables for testing purposes.
// This can be added as params to a method.
decimal amount = 100;
string reference = "";
string invoiceId = ""; // invoice.Id
string customerDisplayName = ""; //customer.DisplayName
string customerId = ""; //customer.Id
string paymentName = "Cash"; // paymentType.Name
string paymentId = "1"; // paymentType.Id
List<Line> lines = new List<Line>(); // invoice.Line
if(lines.Count() == 0)
{
// TODO: You might want to check there are lines?
throw new ArgumentException("No invoice.");
}
Line[] invoiceLines = lines.Select(m => new Line()
{
AnyIntuitObject = m.AnyIntuitObject,
Amount = m.Amount,
AmountSpecified = m.AmountSpecified,
CustomField = m.CustomField,
Id = m.Id,
LineNum = m.LineNum,
Description = m.Description,
DetailType = LineDetailTypeEnum.PaymentLineDetail, //m.DetailType,
DetailTypeSpecified = true, //m.DetailTypeSpecified,
LinkedTxn = new List<LinkedTxn>
{
new LinkedTxn()
{
TxnId = invoiceId,
TxnType = TxnTypeEnum.Invoice.ToString() // TODO: Should this be ReceivePayment?
},
}.ToArray(), //m.LinkedTxn,
LineEx = m.LineEx,
Received = amount, //m.Received,
ReceivedSpecified = true // m.ReceivedSpecified
}).ToArray();
Payment payment = new Payment
{
ProcessPayment = false,
CustomerRef = new ReferenceType { name = customerDisplayName, Value = customerId },
CurrencyRef = new ReferenceType { type = "Currency", Value = "CAD" },
TotalAmt = amount,
TotalAmtSpecified = true,
DepositToAccountRef = new ReferenceType() { Value = "5" },
Line = invoiceLines, // Variable is for debugging purposes - it should be inline or call a method.
PaymentRefNum = reference,
PaymentMethodRef = new ReferenceType()
{
name = paymentName,
Value = paymentId,
}
};
DataService dataService = new DataService(serviceContext);
Payment results = dataService.Add<Payment>(payment);
var json = JsonConvert.SerializeObject(results);
Console.Write(json); // TODO - Use break point/ or write response somewhere for clues.

How to get the property value of the page which is requested by user in episerver cms

How to get the property value of the page which is requested by user in episerver cms 10...
public string GetContent(string pageType, string propertyName)
{
Type type = Type.GetType(pageType); //target type
object o = Activator.CreateInstance(type);
var pageLink = new ContentReference();
var contentLoader= ServiceLocator.Current.GetInstance<IContentLoader>();
var content = contentLoader.Get<type>(pageLink);
var vals = content.GetPropertyValue(propertyName);
return vals;
}
In the above method i have get the page name and property name from the url ....
so in this i have convert the variable pageType ( i.e. page name ) to class and use it in Get<> method..but it is not working...some body please tell me the solution...
or else is there any other way to find property vakue of the user requested property in requeted page.....
I think you're misunderstanding some core concepts.
You should do something like the following:
// Get object used to load Episerver content
var contentLoader = ServiceLocator.Current.GetInstance<IContentLoader>();
// Some content reference
var contentLink = new ContentReference(123);
// Get content of a specific type
var somePage = contentLoader.Get<SomePageType>(contentLink);
// Strongly typed access to content property
var somePropertyValue = somePage.SomeProperty;
If you really have to get a value by its property name:
var someOtherProperty = somePage.GetPropertyValue("SomeOtherPropertyName");
The Answer for this question is :
public string GetContent(string pageName, string propertyName)
{
string content = string.Empty;
try
{
log.Info("GetContent Method is called for getting property value!!!!!");
IContentTypeRepository contentTypeRepository = ServiceLocator.Current.GetInstance<IContentTypeRepository>();
IEnumerable<ContentType> allPageTypes = contentTypeRepository.List().OfType<PageType>();
IContentModelUsage contentModelUsage = ServiceLocator.Current.GetInstance<IContentModelUsage>();
IList<ContentUsage> pageInstanceCollection = new List<ContentUsage>();
foreach (ContentType item in allPageTypes)
{
IList<ContentUsage> pageInstance = contentModelUsage.ListContentOfContentType(item);
foreach (ContentUsage i in pageInstance)
{
pageInstanceCollection.Add(i);
}
}
IEnumerable<ContentUsage> currentpage = pageInstanceCollection.Where(x => x.Name.ToLower() == pageName.ToLower());
int Id = currentpage.First().ContentLink.ID;
PageReference pagereference = new PageReference(Id);
IContentRepository contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>();
PageData pageData = contentRepository.Get<PageData>(pagereference);
content = pageData.GetPropertyValue(propertyName);
}
catch(Exception exception)
{
string errorMessage = string.Format("Error in Content Retrieval : {0}", exception.Message);
log.Error(errorMessage, exception);
}
return content;
}
Here I am passing CMS page name and property name to the method to get the corresponding property value..

How to select multiple column from data base by getting column from user in .net

I have an object in my database, i.e. with 10 attributes.
Now I want to let the user select some of them (1 or 2 up to 10 of them) and then according by user's selection I make a list of object with the attributes selected by user
the scenario that I think about is this:
A page with check boxes that shows the attributes(columns) of that abject then user selects each of them he needs.
But here is my problem, how to make the selected check boxes run as query?
For example user selected col 1 , col 2, col 6 , col 10, how can I write a query responsible for user selection?
Example I wanna the meaningful phrase of this:
var file2 = file.Select(f => new { "attributes selected by user" }).OrderBy(what user wants)
they System.Linq.Dynamic library on Nuget is a way to go
[TestMethod]
public void StringyAndDangerous()
{
var fakePersonDbSet = new List<Person> { new Person() { FirstName = "Some", LastName = "Guy" } }.AsQueryable();
var attributes = new string[] { "FirstName", "LastName" };
var selectedFields = String.Join(",", attributes);
var exprssion = string.Format("new ({0})", selectedFields);
var result = fakePersonDbSet.Select(exprssion, attributes).Cast<dynamic>().First();
}
but you loose type safety and compile time checking. You might be better taking another approach
[TestMethod]
public void SlowerButSafer()
{
var fakePersonDbSet = new List<Person> { new Person() { FirstName = "Some", LastName = "Guy" } }.AsQueryable();
var attributes = new string[] { "FirstName", "LastName" };
var personPropertylist = CovertToKeyValuePair(fakePersonDbSet.First())
.Where(c=> attributes.Contains(c.Key))
.ToArray();
}
private IEnumerable<KeyValuePair<string, object>> CovertToKeyValuePair<T>(T #object)
{
var result = new List<KeyValuePair<string, object>>();
var properties = typeof (T).GetProperties();
foreach (var property in properties)
{
result.Add(new KeyValuePair<string, object>(property.Name, property.GetValue(#object, null)));
}
return result;
}
you'll take a performance hit both for pulling fields from the database that you don't need and for using reflection but the code will be less error prone and you won't end up with errors for trying to select columns that don't exist.
Use DynamicLinq. (link)
Extension methods:
public static T GetValue<T>(this DynamicClass dynamicObject, string propName)
{
if (dynamicObject == null)
{
throw new ArgumentNullException("dynamicObject");
}
var type = dynamicObject.GetType();
var props = type.GetProperties(BindingFlags.Public
| BindingFlags.Instance
| BindingFlags.FlattenHierarchy);
var prop = props.FirstOrDefault(property => property.Name == propName);
if (prop == null)
{
throw new InvalidOperationException("Specified property doesn't exist.");
}
return (T)prop.GetValue(dynamicObject, null);
}
public static string ToDynamicSelector(this IList<string> propNames)
{
if (!propNames.Any())
throw new ArgumentException("You need supply at least one property");
return string.Format("new({0})", string.Join(",", propNames));
}
Usage:
using System.Linq.Dynamic;
// ..
var columns = new[] { "col1", "col2", etc };
var result = context.Files.OrderBy(file => file.Id)
.Select(columns.ToDynamicSelector())
.Cast<DynamicClass>.ToList();
Result will be the collecion of DynamiClass instances wchich columns will contain selected properties.
To get single property from DynamicClass:
var columnValue = result.First().GetValue<string>("col1");
If you want to get values from IEnumerable:
var list = new List<File> { File1, File2, etc.. };
var result = list.AsQueryable().Select( /* the same as above */);

Categories