Getting dynamic list entries from a YAML - c#

My input YAML looks like
menu:
- 'key one': 'first'
- 'key two': 'second'
so quite simple. The sub-keys for menu are arbitrary values so there can be anykey:anyvalue.
Now I'm using YamlReader to get hold of these menu entries in a way that I can deal with key and value one after the other.
In this loop
var yaml = new YamlStream();
yaml.Load(reader);
foreach (var child in ((YamlMappingNode)yaml.Documents[0].RootNode).Children)
{
string cName = child.Key.ToString();
I can access menu. But how can I loop through the kv-pairs in child.value?
(Probably it's something obvious but I really got stuck here.)

We need to iterate the Children collection on the node like:
var mapping = (YamlMappingNode)yaml.Documents[0].RootNode;
var items = (YamlSequenceNode)mapping.Children[new YamlScalarNode("menu")];
foreach (YamlMappingNode item in items)
{
foreach(var child in item.Children)
{
Console.WriteLine(child.Key + " - " + child.Value);
}
}

Related

How do I Get the Children of a DevOps Work Item?

I'm trying to piece together a C# console app that accesses the work items in TFS/DevOps via it's API and compares the original estimate field parent work item with that of all its children and then spits out the name and ID of any work items that do not add up.
So far, I have been able to get back a list of all my work items with the original estimates included, but I still need to get the children of each work item so that I can loop through them and compare the summation of their original estimates with that of their parent. Given how little I know about C# and queries I am pretty stuck right now.
Since linked items are not reportable fields, I have to use $expand to execute a query to get the info I need (at least that's what the doc linked below says). This is where I am stuck. Any tips?
https://learn.microsoft.com/en-us/azure/devops/report/extend-analytics/work-item-links?view=azure-devops
Here is what I have so far.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.Common;
namespace QueryWorkitems0619
{
class Program
{
static void Main(string[] args)
{
string orgName = "{Organization's name}";
string PAT = "{Personal Access Token}";
Uri uri = new Uri($"https://dev.azure.com/{orgName}");
string project = "Wingnit_2";
VssBasicCredential credentials = new VssBasicCredential("", PAT);
//create a wiql object and build our query
Wiql wiql = new Wiql()
{
Query = "Select * " +
"From WorkItems " +
"Where [System.TeamProject] = '" + project + "' " +
"And [System.State] <> 'Closed' " +
"And [System.RelatedLinkCount] > '0'" +
"Order By [State] Asc, [Changed Date] Desc"
};
//create instance of work item tracking http client
using (WorkItemTrackingHttpClient workItemTrackingHttpClient = new WorkItemTrackingHttpClient(uri, credentials))
{
//execute the query to get the list of work items in the results
WorkItemQueryResult workItemQueryResult = workItemTrackingHttpClient.QueryByWiqlAsync(wiql).Result;
//some error handling
if (workItemQueryResult.WorkItems.Count() != 0)
{
//need to get the list of our work item ids and put them into an array
List<int> list = new List<int>();
foreach (var item in workItemQueryResult.WorkItems)
{
list.Add(item.Id);
}
int[] arr = list.ToArray();
//build a list of the fields we want to see
string[] fields = new string[5];
fields[0] = "System.Id";
fields[1] = "System.Title";
fields[2] = "System.RelatedLinkCount";
fields[3] = "System.Description";
fields[4] = "Microsoft.VSTS.Scheduling.OriginalEstimate";
//get work items for the ids found in query
var workItems = workItemTrackingHttpClient.GetWorkItemsAsync(arr, fields, workItemQueryResult.AsOf).Result;
//loop though work items and write to console
foreach (var workItem in workItems)
{
foreach (var field in workItem.Fields)
{
Console.WriteLine("- {0}: {1}", field.Key, field.Value);
}
}
Console.ReadLine();
}
}
}
}
}
You are in the right direction, you need to add the expand when you execute the GetWorkItemsAsync method:
var workItems = workItemTrackingHttpClient.GetWorkItemsAsync(arr, expand: WorkItemExpand.Relations workItemQueryResult.AsOf).Result;
Note: you can't use fields with expand together, so you need to remove it (you will get all the fields).
Loop the results, inside the work item result you will see Relations, check inside the Relations if the Rel is System.LinkTypes.Hierarchy-Forward, if yes - it's a child:
Now, you have the child id inside the Url, extract it and make an API to get his details.

C# - Public string only stores last value outside of collection

I am using HtmlAgilityPack to find all items, colours and links to products on a website. I want to be able to find an item on the website by typing in the name and colour inside my application.
So far what I have working is:
The application finds items using only the item name and returns the last thing on the website with that name. There are multiple products with the same name but each have a different colour.
The problem comes in when including colour because it's in a different XPath so it's stored in a different collection.
Here is my code:
HtmlNodeCollection collection = doc.DocumentNode.SelectNodes("//*[contains(#class,'inner-article')]//h1//a");
HtmlNodeCollection collection2 = doc.DocumentNode.SelectNodes("//*[contains(#class,'inner-article')]//p//a");
foreach (var node2 in collection2)
{
string coloursv = node2.InnerHtml.ToString();
strColour = coloursv;
//txtLog.Text += Environment.NewLine + (DateTime.Now.ToString("hh:mm:ss")) + str; - This code returns all colours (If code is ran outside of collection then only last colour in string is returned.
}
foreach (var node in collection)
{
string href = node.Attributes["href"].Value;
var itemname = node.InnerHtml.ToString();
if (itemname.Contains(txtKeyword.Text))
{
txtLog.Text = (DateTime.Now.ToString("hh:mm:ss")) + " - Item Found: " + href + " " + itemname + " " + strColour; //Successfully returns item name, colour and link but always gives last availible on website
}
}
This is because you are continually setting the Text property of a textbox within a loop (so each item will continually overwrite the previous):
foreach (var node in collection)
{
// Omitted for brevity
// This will continually overwrite the contents of your Text property
txtLog.Text = ...;
}
If you want to store multiple items, you'll either need to store the results in some type of a collection object (such as a ListBox, etc.) or by simply concatenating your values into the textbox:
foreach (var node in collection)
{
// Omitted for brevity
var stringToAdd = ...;
txtLog.Text += stringToAdd + Environment.NewLine;
}
You can also accomplish this by using the StringBuilder class to be a bit more efficient:
StringBuilder sb = new StringBuilder();
foreach (var node in collection)
{
// Omitted for brevity
var stringToAdd = ...;
// Append this item to the results
sb.AppendLine(stringToAdd);
}
// Store the results
txtLog.Text = sb.ToString();

More efficent way of finding the index of an elements' class in `List`

So I have this code where I gather the elements then write all the class names to a List then find out the index of that specific class.
This to me seems very longwinded, maybe I should use linq however my brain cannot fathom another way to do it (it is friday!!).
private void Pagination()
{
List<string> classnames = new List<string> { };
var pagination = _driver.FindElements(By.CssSelector("ul[data-type='order-sitebundle']> li"));
//var activepage = _driver.FindElement(By.CssSelector("ul[data-type='order-sitebundle']> li[class='active']"));
foreach (var item in pagination)
{
classnames.Add(item.GetAttribute("className"));
}
int actibve = classnames.IndexOf("active");
pagination[actibve++].Click();
Find the element that is active:
ul[data-type='order-sitebundle'] li[class='active']
You don't have to use actibve++, find the next li after the active one:
ul[data-type='order-sitebundle'] li[class='active'] + li

How do I access the elements of an array stored in a dictionary?

Given an array of strings stored within a dictionary, I'd like to access the first element stored in the array. How would I go about doing that?
Here's what I have already written...
public IC2Engineering GetReportResultsTableByXPath(string xPath, int rowNumber) {
WaitForComplete();
IWebElement table = FindElement(By.XPath(xPath));
//get an array of rows from the table
IList<IWebElement> table_rows = table.FindElements(By.TagName("tr"));
//store the row data
IList<IWebElement> row_data = table_rows[rowNumber].FindElements(By.XPath("td"));
int col_num;
col_num = 0;
foreach (IWebElement cell in row_data) {
Console.WriteLine("col #" + col_num + ": " + row_data[col_num].Text.ToString());
col_num++;
}
return this;
}
So I know that the strings are being stored properly, because the data written to the console output is correct.
How can I store the data from each one of those outputs into it's own separate "spot" in a SharedProperty Dictionary called "TableRowData"?
And after storing those outputs into their own location within the dictionary, how can I access that first entry in the dictionary?
I have a strong feeling that the code I am looking for must execute within the foreach loop, but I am can not figure out how to do that...
If I understand you right, the problem is here:
SeleniumUITests.SharedProperties["ReportRowData"] = row_data.ToString();
You are storing the "array" (really a List<T>) as a string in the dictionary, not the array itself. It seems like you want:
SeleniumUITests.SharedProperties["ReportRowData"] = row_data;
Console.WriteLine(SeleniumUITests.SharedProperties["ReportRowData"][0]);

List, strings and vs Template

I'm having a list of strings whit some values and I want to make some kind of variable for keeping code that I will be using in template file.
For example lets say I have list with this 3 string values: configService, scaleCoefConfigService, sessionService. Name of the list is chItemName.
And I need to generate this kind of code that I will parse later into template:
[Dependency("configService")]
[Dependency("scaleCoefConfigService")]
[Dependency("sessionService")]
So my question is can make some variable and mechanism for iterating thou list of strings that adds every single item from list to variable?
I've tried this:
foreach (var tp in controllerChecked)
{
var genCode = "[Dependency](" '"' + chItemName + '"'")] \n"
}
controllerChecked is collection of objects and one of the objects value is Name that I'm getting like this:
var chItemName = controllerChecked.Select(c => c.Name).ToList();
This is how the list chItemName is getting those strings.
But of course it is impossible to use + with lists and this kind of stuff will never work. Someone has better idea?
In your example, you are not using the tp variable, which contains will contain each of the values within controllerChecked, one at a time.
You could just iterate through the chItemName list and add the result to a StringBuilder:
StringBuilder codeBuilder = new StringBuilder();
foreach (string tp in chItemName)
{
codeBuilder.AppendLine("[Dependency(\"" + tp + "\")]");
}
string code = codeBuilder.ToString();
If controllerChecked contains more information, you could also directly access it:
StringBuilder codeBuilder = new StringBuilder();
foreach (var item in controllerChecked)
{
string propertyName = item.Name.SubString(1);
codeBuilder.AppendLine("[Dependency(\"" + item.Name + "\")]");
codeBuilder.AppendLine("public " + item.Type + " " + propertyName + " { get; set; }");
codeBuilder.AppendLine();
}
string code = codeBuilder.ToString();
PS. I would definitely change the name of chItemName to chItemNames as it is a list, but that is up to you of course.
This worked perfectly good. I have little bit harder version of this, if you can figure out how to do this:
Lets say that instead of one chItemName list I have 2 more: fName and chItemType, both are string lists.
And I have to generate this kind of code:
[Dependency("alarmsService")]
public IAlarmsService AlarmsService { get; set; }
[Dependency("jsonFactory")]
public IJSONFactoryService JsonFactory { get; set; }
[Dependency("dataBean")]
public IDataBean DataBean { get; set; }
alarmsServise, jsonFactory and dataBean are items of chItemName.
IAlarmsService, IJSONFactoryService and IDataBean are items of chItemType.
AlarmsService, Json Factory and DataBean are items of fName list.
fName is list that I got from chItemType by trimming the first letter from each string in list:
List<string> fName = new List<string>();
foreach(var i in chItemType)
{
var newName = i.Remove(0,1);
fName.Add(newName);
}
So only that list is not a part of controllerChecked list. The othere two are defined like this:
var chItemType = controllerChecked.Select(c => c.Type).ToList();
var chItemName = controllerChecked.Select(c => c.Name).ToList();
Can I edit foreach somehow or maybe I can make parts of code with StringBulider and after that merged them together?

Categories

Resources