Looping through a table twice - laravel

I'm trying to create a page that has a table. The table has a service and then the items under the service so the table should look like this
<table class="table table-striped table-bordered table-condensed pricing_table">
<tr>
<td></td>
<td>
Short
</td>
<td>
Medium
</td>
<td>
Long
</td>
</tr>
<tr>
<td>
Cut & Blow Dry
</td>
<td>
R170
</td>
<td>
R190
</td>
<td>
R220
</td>
</tr>
<tr>
<td>
Blow Dry
</td>
<td>
R120
</td>
<td>
R170
</td>
<td>
R190
</td>
</tr>
<tr>
<td>
Girls under 18
</td>
<td>
R130
</td>
<td></td>
<td></td>
</tr>
<tr>
<td>
Pensioners
</td>
<td>
R130
</td>
<td></td>
<td></td>
</tr>
<tr>
<td>
Gents Cut
</td>
<td>
R120
</td>
<td></td>
<td></td>
</tr>
<tr>
<td>
Boys Cut
</td>
<td>
R90
</td>
<td></td>
<td></td>
</tr>
<tr>
<td>
Upstyles - Trail
</td>
<td>
R270
</td>
<td></td>
<td></td>
</tr>
<tr>
<td>
Upstyles
</td>
<td>
R350
</td>
<td></td>
<td></td>
</tr>
</table>
but what I'm getting is this
<table class="table table-striped table-bordered table-condensed pricing_table">
<tbody>
<tr>
<td> Cutting & Styling </td>
<td> Short </td>
<td> Medium </td>
<td> Long </td>
</tr>
<tr>
<td> Cut & Blow Dry </td>
<td> 150 </td>
<td> 170 </td>
<td> 190 </td>
</tr>
<tr>
<td> Cutting & Styling </td>
<td> Short </td>
<td> Medium </td>
<td> Long </td>
</tr>
<tr>
<td> Blow Dry </td>
<td> 100 </td>
<td> 120 </td>
<td> 140 </td>
</tr>
<tr>
<td> Cutting & Styling </td>
<td> Short </td>
<td> Medium </td>
<td> Long </td>
</tr>
<tr>
<td> Girls under 18 </td>
<td> 120 </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> Cutting & Styling </td>
<td> Short </td>
<td> Medium </td>
<td> Long </td>
</tr>
<tr>
<td> Pensioners (Ladies) </td>
<td> 70 </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> Chemical Service </td>
<td> Short </td>
<td> Medium </td>
<td> Long </td>
</tr>
<tr>
<td> Color </td>
<td> 150 </td>
<td> 200 </td>
<td> 250 </td>
</tr>
<tr>
<td> Chemical Service </td>
<td> Short </td>
<td> Medium </td>
<td> Long </td>
</tr>
<tr>
<td> Half Head Foils </td>
<td> 200 </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> Chemical Service </td>
<td> Short </td>
<td> Medium </td>
<td> Long </td>
</tr>
<tr>
<td> Full Head Foils </td>
<td> 300 </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> Chemical Service </td>
<td> Short </td>
<td> Medium </td>
<td> Long </td>
</tr>
<tr>
<td> Per Foil </td>
<td> 10 </td>
<td> 15 </td>
<td> 20 </td>
</tr>
<tr>
<td> Chemical Service </td>
<td> Short </td>
<td> Medium </td>
<td> Long </td>
</tr>
<tr>
<td> Brazilian </td>
<td> 700 </td>
<td> 800 </td>
<td> 900 </td>
</tr>
<tr>
<td> Chemical Service </td>
<td> Short </td>
<td> Medium </td>
<td> Long </td>
</tr>
<tr>
<td> Perm </td>
<td> 150 </td>
<td> 170 </td>
<td> 190 </td>
</tr>
<tr>
<td> Treatment </td>
<td> Short </td>
<td> Medium </td>
<td> Long </td>
</tr>
<tr>
<td> Salon Treatment </td>
<td> 100 </td>
<td> </td>
<td> </td>
</tr>
<tr>
<td> Treatment </td>
<td> Short </td>
<td> Medium </td>
<td> Long </td>
</tr>
<tr>
<td> Olaplex - Stand Alon </td>
<td> 180 </td>
<td> </td>
<td> </td>
</tr>
</tbody>
</table>
The service is always being looped and displayed. I would only like the service to be looped once and the items to be displayed under their respective service.
My pricing.blade.php
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 about_content_area">
<h1>pricing</h1>
<div class="col-lg-5 col-lg-offset-3 col-md-5 col-md-offset-3 pricing_wrapper">
<table class="table table-striped table-bordered table-condensed pricing_table">
#foreach($services_options as $services)
#foreach($services->service as $service)
<tr>
<td>
{!! $service->title !!}
</td>
<td>
Short
</td>
<td>
Medium
</td>
<td>
Long
</td>
</tr>
#endforeach
<tr>
<td>
{!! $services->title !!}
</td>
<td>
{!! $services->short !!}
</td>
<td>
{!! $services->medium !!}
</td>
<td>
{!! $services->long !!}
</td>
</tr>
#endforeach
</table>
</div>
</div>
</div>
My controller
public function content($id)
{
$menus_child = Menu::where('menu_id', 0)->with('menusP')->get();
$menu = Menu::where('id', $id)->firstOrFail();
$layout = $menu->type;
$gallery_category = Gcategory::all();
$services_options = Price::all();
return view('open::public/'.$layout, compact('menus_child', 'menu', 'gallery_category', 'services_options'));
}

You have a loop for the header part of the table (the short, medium, long part) within the loop for each service, which is why its being output above each service row.
You just need to update pricing.blade.php to be the following
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 about_content_area">
<h1>pricing</h1>
<div class="col-lg-5 col-lg-offset-3 col-md-5 col-md-offset-3 pricing_wrapper">
<table class="table table-striped table-bordered table-condensed pricing_table">
<tr>
<td></td>
<td>
Short
</td>
<td>
Medium
</td>
<td>
Long
</td>
</tr>
#foreach($services_options as $services)
<tr>
<td>
{!! $services->title !!}
</td>
<td>
{!! $services->short !!}
</td>
<td>
{!! $services->medium !!}
</td>
<td>
{!! $services->long !!}
</td>
</tr>
#endforeach
</table>
</div>
</div>
</div>

Related

List only records populated with Capybara

Friends helped me with a solution that validates if there are [active/inactive] records in the list. When I list the records using pp capybara also returns blank lines. How do I disregard empty records?
def validate_active_inactive_records
expect(page).to have_css("td:nth-child(5)", :text => /^(ACTIVE|INACTIVE)$/)
# ***listing records***
page.all('.tvGrid tr > td:nth-child(5)').each do |td|
puts td.text
end
end
<table width="100%" class="tvGrid">
<tbody>
<tr>
<th colspan="1" class="tvHeader">Id</th>
<th colspan="1" class="tvHeader">Code</th>
<th colspan="1" class="tvHeader">Description</th>
<th colspan="1" class="tvHeader">Operational Center</th>
<th colspan="1" class="tvHeader">Status</th>
</tr>
<tr class="tvRowEmpty">
<td> </td>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr class="tvRowEmpty">
<td> </td>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr class="tvRowEmpty">
<td> </td>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr class="tvRowEmpty">
<td> </td>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr class="tvRowEmpty">
<td> </td>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr class="tvRowEmpty">
<td> </td>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr class="tvRowEmpty">
<td> </td>
<td> </td>
<td> </td>
<td> </td>
<td> </td>
</tr>
</tbody>
</table>
Are you asking how to remove the rows with the class tvRowEmpty from your search results? If so, you can use the :not operator in your finder:
def validate_active_inactive_records
expect(page).to have_css("td:nth-child(5)", :text => /^(ACTIVE|INACTIVE)$/)
# ***listing records***
page.all('.tvGrid tr:not(.tvRowEmpty) > td:nth-child(5)').each do |td|
puts td.text
end
end
If you want to exclude any td that just contains you could use the following finder with a regex that filters tags containing only whitespace characters:
page.all('.tvGrid tr > td:nth-child(5)', text: /[\s]^*/).each

Thymeleaf th:block condition

I want to set up some conditions on Thymeleaf templates like this, but it doesn't work.
<table border=2>
<thead>
<tr>
<td> Identifiant </td>
<td> Nom Formation </td>
<td> Descirption Formation </td>
<td> Adresse Formation </td>
<td>Status Formation </td>
<td> Chef Projet </td>
<td> Formateur </td>
<td>Ressource Humain</td>
<td>Update</td>
<td>Liste Devellopeur</td>
</tr>
</thead>
<tbody>
<tr th:each="formations : ${formations}">
<th:block th:if="${StatusFormation}} =='Traitement' }">
<td th:text="${formations.id}"> </td>
<td th:text="${formations.NomFormation}"> </td>
<td th:text="${formations.DescriptionFormation}"> </td>
<td th:text="${formations.StatusFormation}"> </td>
<td th:text="${formations.AdresseFormation}"> </td>
<td th:text="${formations.chef_projet}"> </td>
<td th:text="${formations.formateurs}"> </td>
<td th:text="${formations.ressourcehumain}"> </td>
</th:block>
</tr>
</tbody>
</table>
the erros is
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as expression: "${StatusFormation}} =='Traitement' }" (template: "ChefProjetFormationHome" - line 29, col 11) at org.thymeleaf.standard.expression.StandardExpressionParser.parseExpression(StandardExpressionParser.java:131)
The problem is you add one extra brace in this line:
<th:block th:if="${StatusFormation}} =='Traitement' }">
you should change it to:
<th:block th:if="${StatusFormation} == 'Traitement'">

Check the check box in a table

In my application I have check any one of the check box and click on save button. I used with table as follows to select the option, but it doesnt work:
# with in table, set
within_table('countryTable') do
find(:xpath, "//tbody/tr/td[1]/checkbox").set(true)
end
click_button('Save')
but its not working.....
Select countries for this owner
<tr>
<td class='first'>
<input type='checkbox' id='CheckboxAU' name='AU' value='AU' />
</td>
<td>
Australia
</td>
</tr>
<tr>
<td class='first'>
<input type='checkbox' id='CheckboxCA' name='CA' value='CA' />
</td>
<td>
Canada
</td>
</tr>
<tr>
<td class='first'>
<input type='checkbox' id='CheckboxFR' name='FR' value='FR' />
</td>
<td>
France
</td>
</tr>
<tr>
<td class='first'>
<input type='checkbox' id='CheckboxGG' name='GG' value='GG' />
</td>
<td>
Guernsey
</td>
</tr>
<tr>
<td class='first'>
<input type='checkbox' id='CheckboxJP' name='JP' value='JP' />
</td>
<td>
Japan
</td>
</tr>
<tr>
<td class='first'>
<input type='checkbox' id='CheckboxNZ' name='NZ' value='NZ' />
</td>
<td>
New Zealand
</td>
</tr>
<tr>
<td class='first'>
<input type='checkbox' id='CheckboxZA' name='ZA' value='ZA' />
</td>
<td>
South Africa
</td>
</tr>
<tr>
<td class='first'>
<input type='checkbox' id='CheckboxCH' name='CH' value='CH' />
</td>
<td>
Switzerland
</td>
</tr>
<tr>
<td class='first'>
<input type='checkbox' id='CheckboxAE' name='AE' value='AE' />
</td>
<td>
United Arab Emirates
</td>
</tr>
<tr>
<td class='first'>
<input type='checkbox' id='CheckboxGB' name='GB' value='GB' />
</td>
<td>
United Kingdom
</td>
</tr>
<tr>
<td class='first'>
<input type='checkbox' id='CheckboxUS' name='US' value='US' />
</td>
<td>
United States
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</table>
You can reach the check box by id, css, or xpath.
# to check the FR with xpath
find(:xpath, "//input[#name='FR']").click()
# to check the FR with id
find(:id, "CheckboxFR").click()
# to check the FR with css
find(:css, "#CheckboxFR").click()
if you want to use within_table which narrow the scope:
within_table(find(:id, "table_id")) do
find(:id, "id_of_option").click()
end

Scraping info of a product that is spread among different <tr> elements?

I'm using something like the following to scrape the info of a page:
def self.parse_products
product_hash = {}
product = #data.css('.simGrid')
product.css('td').each do | product |
product_asin = product.css('.simImage a img').first.value[/(?<=\/)[A-Z\d]{5,}/]
product_image_url = product.css('.simProductInfo a').to_s
product_hash[:product] ||= []
product_hash[:product] << { :image_url => product_image_url,
:asin => product_asin }
end
product_hash
end
The problem is that the structure is something like this:
<table class="simGrid">
<tbody>
<tr class="middle">
<td>
<div class="simImage"></div>
</td>
<td>
<div class="simImage"></div>
</td>
<td>
<div class="simImage"></div>
</td>
</tr>
<tr>
<td>
<div class="simProductInfo"></div>
</td>
<td>
<div class="simProductInfo"></div>
</td>
<td>
<div class="simProductInfo"></div>
</td>
</tr>
<tr>
<td>
<hr class="divider" />
</td>
<td>
<hr class="divider" />
</td>
<td>
<hr class="divider" />
</td>
</tr>
<tr class="middle">
<td>
<div class="simImage"></div>
</td>
<td>
<div class="simImage"></div>
</td>
<td>
<div class="simImage"></div>
</td>
</tr>
<tr>
<td>
<div class="simProductInfo"></div>
</td>
<td>
<div class="simProductInfo"></div>
</td>
<td>
<div class="simProductInfo"></div>
</td>
</tr>
<tr>
<td>
<hr class="divider" />
</td>
<td>
<hr class="divider" />
</td>
<td>
<hr class="divider" />
</td>
</tr>
</tbody>
</table>
So as you can see the info of the product is spread among various <tr>. If I try to scrape them by using <td> I end up with many nil values since some of the <td> have the .simImage and others don't. Same for the .simProductInfo.
As anyone encounter something similar before? Is there any workaround for this?
You can try collecting ASINs and URLs in two separate arrays and then zipping them.
asins = product.css('.simImage a img').map { |n| n.value[/(?<=\/)[A-Z\d]{5,}/] }
urls = product.css('.simProductInfo a').map(&:to_s)
asins.zip(urls).map { |asin, url| {image_url: url, asin: asin} }

Project DataTable into new structure with Linq

I have a DataTable I would like massage into a new format (here is what it comes out like when attached to a gridview):
<table cellspacing="0" rules="all" border="1" id="GridView1" style="border-collapse: collapse;">
<tr>
<th scope="col">
Line
</th>
<th scope="col">
StartTime
</th>
<th scope="col">
EndTime
</th>
<th scope="col">
Attribute
</th>
<th scope="col">
Value
</th>
</tr>
<tr>
<td>
Line1
</td>
<td>
24/01/2013 7:30:10 AM
</td>
<td>
24/01/2013 8:00:10 AM
</td>
<td>
Actual
</td>
<td>
0
</td>
</tr>
<tr>
<td>
Line1
</td>
<td>
24/01/2013 7:30:10 AM
</td>
<td>
24/01/2013 8:00:10 AM
</td>
<td>
ProductCategory
</td>
<td>
FFAC
</td>
</tr>
<tr>
<td>
Line1
</td>
<td>
24/01/2013 7:30:10 AM
</td>
<td>
24/01/2013 8:00:10 AM
</td>
<td>
Target
</td>
<td>
36.5
</td>
</tr>
<tr>
<td>
Line2
</td>
<td>
24/01/2013 7:26:50 AM
</td>
<td>
24/01/2013 8:00:10 AM
</td>
<td>
Actual
</td>
<td>
69
</td>
</tr>
<tr>
<td>
Line2
</td>
<td>
24/01/2013 7:26:50 AM
</td>
<td>
24/01/2013 8:00:10 AM
</td>
<td>
ProductCategory
</td>
<td>
FFAC
</td>
</tr>
<tr>
<td>
Line2
</td>
<td>
24/01/2013 7:26:50 AM
</td>
<td>
24/01/2013 8:00:10 AM
</td>
<td>
Target
</td>
<td>
55.5555582046509
</td>
</tr>
<tr>
<td>
Line3
</td>
<td>
24/01/2013 8:00:20 AM
</td>
<td>
24/01/2013 8:47:50 AM
</td>
<td>
Actual
</td>
<td>
1475
</td>
</tr>
<tr>
<td>
Line3
</td>
<td>
24/01/2013 8:00:20 AM
</td>
<td>
24/01/2013 8:47:50 AM
</td>
<td>
ProductCategory
</td>
<td>
FFAC
</td>
</tr>
<tr>
<td>
Line3
</td>
<td>
24/01/2013 8:00:20 AM
</td>
<td>
24/01/2013 8:47:50 AM
</td>
<td>
Target
</td>
<td>
202.430557310581
</td>
</tr>
<tr>
<td>
Line4
</td>
<td>
24/01/2013 7:31:30 AM
</td>
<td>
24/01/2013 8:00:10 AM
</td>
<td>
Actual
</td>
<td>
1384
</td>
</tr>
<tr>
<td>
Line4
</td>
<td>
24/01/2013 7:31:30 AM
</td>
<td>
24/01/2013 8:00:10 AM
</td>
<td>
ProductCategory
</td>
<td>
FFAC
</td>
</tr>
<tr>
<td>
Line4
</td>
<td>
24/01/2013 7:31:30 AM
</td>
<td>
24/01/2013 8:00:10 AM
</td>
<td>
Target
</td>
<td>
3179.26381587982
</td>
</tr>
<tr>
<td>
Line5
</td>
<td>
24/01/2013 7:37:00 AM
</td>
<td>
24/01/2013 8:00:10 AM
</td>
<td>
Actual
</td>
<td>
0
</td>
</tr>
<tr>
<td>
Line5
</td>
<td>
24/01/2013 7:37:00 AM
</td>
<td>
24/01/2013 8:00:10 AM
</td>
<td>
ProductCategory
</td>
<td>
FHHT
</td>
</tr>
<tr>
<td>
Line5
</td>
<td>
24/01/2013 7:37:00 AM
</td>
<td>
24/01/2013 8:00:10 AM
</td>
<td>
Target
</td>
<td>
92.6652171770756
</td>
</tr>
<tr>
<td>
P2_Bundler
</td>
<td>
24/01/2013 7:35:00 AM
</td>
<td>
24/01/2013 8:00:10 AM
</td>
<td>
Actual
</td>
<td>
7
</td>
</tr>
</table>
I know that if the records have the same line, start and end times, the records are related.
What I want to do is get a sum of the Actual and Target values grouped by the ProductCategory. In other words:
ProductCategory | Sum(Actual) | Sum(Target)
FFAC | 1000 | 2000
FHHT | 200 | 175
Any guidance would be appreciated!
Regards,
Chris
You won't have any massage here ;)
The structure of your dataTable would be more usefull than the grid code, but to get what you want from your DataTable, you should do something like that.
var result = myDataTable.AsEnumerable()
.GroupBy(m => m.Field<string>("ProductCategory"))
.Select(g => new {
productCategory = g.Key,
sumActual = g.Sum(x => x.Field<decimal>("Actual")),
sumTarget = g.Sum(x => x.Field<decimal>("Target"))
});
Oi, I thought this was going to be more difficult than anticipated, but it ended up being easier and I rewrote this. You perform your grouping on the line, but then you just have to query the rows in each group to get the ProductCategory.
First, you're going to group by the main key, which in this case I believe is the Line column. So:
myDataTable.AsEnumerable()
.GroupBy(m => m.Field<string>("Line"))
Next, we're going to have to find the category for each of these groupings. Since each grouping is an IEnumerable<T>, just perform a Select after filtering our Attribute as ProductCategory and get the first value. I use a little "defensive" coding by accounting for the situation where no ProductCategory attribute exists:
...
.Select(g => new
{
ProductCategory = g.Where(r => r.Field<string>("Attribute") == "ProductCategory")
.Select(r => r.Field<string>("Value"))
.FirstOrDefault() ?? "No Category",
SumActual = g.Sum(x => x.Field<decimal>("Actual")),
SumTarget = g.Sum(x => x.Field<decimal>("Target"))
})
Edit: Alright, so now I see what you're saying. My original thoughts were correct in that you're grouping each original group (based on Line) into further groups. There really isn't an easy way, and the options aren't pretty. It's the fact that you have to group each group, but in order to group the group, you have to query the group for a single entity, then aggregate based on another 2 entities (making it very difficult to use LINQ here).
myDataTable.AsEnumerable()
.GroupBy(m => m.Field<string>("Line"))
.Select(g => new
{
ProductCategory = g.Where(r => r.Field<string>("Attribute") == "ProductCategory")
.Select(r => r.Field<string>("Value"))
.FirstOrDefault() ?? "No Category",
Actual = g.Where(r => r.Field<string>("Attribute") == "Actual")
.Select(r =>
{
decimal d = 0m;
Decimal.TryParse(r.Field<string>("Value"), out d);
return d;
}
.FirstOrDefault(),
Target = g.Where(r => r.Field<string>("Attribute") == "Target")
.Select(r =>
{
decimal d = 0m;
Decimal.TryParse(r.Field<string>("Value"), out d);
return d;
}
.FirstOrDefault()
})
.GroupBy(n => n.ProductCategory)
.Select(g => new
{
ProductCategory = g.Key,
SumActual = g.Sum(x => x.Actual),
SumTarget = g.Sum(x => x.Target)
})
Again, not very pretty... Especially since your "Value" column is string, you have to parse them in order to get a meaningful value (in this case, summing a number). The premise here is that you group all your records into "blocks", then compose these blocks into single anonymous objects. So each anonymous object represents all datarow Attribute/Value pairs for a particular Line. From there, you just group based on your desired key (ProductCategory in this case), and perform your required aggregates.
PS, I would actually perform this query on the DB side using a PIVOT. Might take a bit more setup, but then you give the system designed to perform this type of data crunching/aggregation the work that it can handle, and keep your front-end CPU cycles for something more important.

Resources