Finding xpath siblings after declaring a variable with find_elements - python-2.7

I start by trying to find all menu items on a site by selecting them with a .find_elements_by_xpath. This works fine
(The buttons are either text or an image).
Then I want to loop through each of these elements and return either the text between the tags or the src of the image of a span tag which preceeds the tag inbetween which there is text.
Returning the text works fine but I am unable to return the src. I am having trouble building an xpath which roots from the current iteration of the loop. What I am left with is either an 'unable to locate' or I return the first menu image over and over again.
Here is the code I currently have running (note I am unable to give out the URL to the site):
browser = webdriver.Chrome(...)
menu = browser.find_elements_by_xpath('//td[#onmouseover]')
for menu_part in menu:
try:
if len(menu_part.text) < 2:
menu_button = menu_part.find_element_by_xpath(
'/span[#class="ThemeOfficeMainFolderText"]/preceding-sibling::span/img').get_attribute('src')
else:
menu_button = menu_part.text
print menu_button
except Exception as e:
print e
pass
I am unsure if the syntax is completely correct/ if I can use the currently iterated element as the 'root' of my find_element function (menu_part.find_element_by_xpath)
Also, there is no way to further specify the tags with attributes because all menu items have identical attributes.
Lastly, the following code returns the first image in the menu.
menu_button = browser.find_element_by_xpath(
'//span[#class="ThemeOfficeMainFolderText"]/preceding-sibling::span/img').get_attribute('src')
Therfore, I am relatively confident the code following "span[#class... " works fine, the issue is the preceding code.
I am hopeful that there is a simple solution and that I made a mistake while writing the xpath, but I am completely out of ideas at the moment...
EDIT:
here is the basic html structure I am dealing with
<td class="ThemeOfficeMainItem" onmouseover="ItemMouseOverOpenSub ()">
<span class="ThemeOfficeMainFolderLeft">
<img src="img1.png"></span>
<span class="ThemeOfficeMainFolderText">TEXT</span>
<span class="ThemeOfficeMainFolderRight"> </span>
</td>
<td class="ThemeOfficeMainItem" onmouseover="ItemMouseOverOpenSub ()">
<span class="ThemeOfficeMainFolderLeft">
<img src="img2.png"></span>
<span class="ThemeOfficeMainFolderText"></span>
<span class="ThemeOfficeMainFolderRight"> </span>
</td>

If you want to search for span starting from previously defined parent element menu_part, then you should use
./span[#class="ThemeOfficeMainFolderText"]/preceding-sibling::span/img
Note the dot at the beginning of XPath that points to current (menu_part) element
Update
As for the logic of your code, try below:
browser = webdriver.Chrome()
browser.get(URL)
menu = browser.find_elements_by_xpath('//td[#onmouseover]')
for menu_part in menu:
text_span = menu_part.find_element_by_xpath('./span[#class="ThemeOfficeMainFolderText"]')
if not text_span.text:
menu_button = menu_part.find_element_by_xpath('./span[#class="ThemeOfficeMainFolderText"]/preceding-sibling::span/img').get_attribute('src')
else:
menu_button = text_span.text
print menu_button

Related

AEM property displays correctly in <p> but not in href attribute

I'm making a custom multifield breadcrumb component with this code.
<ul class="breadcrumb-list">
<li class="breadcrumb-item" data-sly-repeat.textItem="${properties.text}">
<a class="breadcrumb-link" href="${properties.link['textItemList.index']}">${textItem}</a>
<p class="works">${properties.link[textItemList.index]}</p>
<p class="doesn't work">${properties.link['textItemList.index']}</p>
<span>//</span>
</li>
</ul>
I've added the paragraph elements to show how my properties display.
This is the html output:
If I remove the single quotes as in p class="works", the value displays the breadcrumb item's link value. If I add the single quotes as in p class="doesn't work", nothing displays.
You'd think that removing the quotes from the same value in the href would display my link as the href; however, when i remove the quotes in the href value, it removes the anchor tag altogether. when i add them back in, i can still see the anchor tag, but there is no href attribute at all, only the breadcrumb-link class.
How can I get the href value to show a link?
AEM evaluates al href before rendering the resulting html.
If the link is invalid it will not put anything under href
If the expression is invalid it will wipe out all the section
Have you tested with valid links?
This will not work only with plain text.
when i add them back in, i can still see the anchor tag, but there is no href attribute at all, only the breadcrumb-link class. - That is the expected behaviour. Because you have an array of links and putting the quotes will try to obtain an entry in the array by the 'textItemList.index' key, like you would do in a map, instead by the index. Nothing is found of course, so the anchor point is rendered without the href attribute.
when i remove the quotes in the href value, it removes the anchor tag altogether. - That is not the expected behaviour. Since you exemplify that inside the paragraph it works, then I suppose this is a display context issue. HTL should implicitly uses the uri context for href attribute values and text for content inside HTML elements according to the documentation.
Try explicitly mention the context like href="${properties.link[textItemList.index] # context = 'uri'}" or href="${properties.link[textItemList.index] # context = 'text'}", or check the contexts mentioned in the doc - unsafe should ultimately do the job.
None of this context worked for me, and Im facing the same issue, if I remove the single quote "'" it will display the href just ok, but since my href includes it none of the context mentioned above is doing the job.
Is there any solution for this out there?

Bootstrap popover only shows one word of string

I have a set of popovers that show supplemental info upon hovering. They display, but only the first word of the string.
template:
<a data-toggle="popover" data-trigger="hover" data-content={{std.Description}} > <i class="fa fa-question"></i></a>
...
$("[data-toggle=popover]").popover();
The field in question is a Django TextField:
Description = models.TextField(blank=True)
Behavior:
This description had several words, but all after the first were cut off.
The solution is the same as for Contain form within a bootstrap popover? , though the title of the post may be misleading. The issue is that there need to be quotes containing the desired variable:
data-content="{{std.Description}}"
Additionally, if you want to include html in the popover (lists, <br>, etc.), include data-html="true".

Selenium Python I am getting an empty string from a text element but there is a value on the GUI

I have some text displayed on the GUI. I have identified the element using it's XPATH from Xpath Checker from Firebug. The element highlights correctly.
In my code i use the text attribute to to print out it's value.
I would like to print the value to the console.
When i walk through the code using PyCharm debugger I can see the variable text attribute is empty.
Why has the variable not got the text value?
My code snippet is:
def is_engine_number_displayed(self):
engine_no_element = self.get_element(By.XPATH, '//a[contains(., "Engine: 5.1.1")]')
print "engine_no_element.text***"
print engine_no_element.text
#return engine_no_element.text in "Engine: 5.1.1"
The HTML is:
<div class="GJPPK2LBKQ">
<a class="gwt-Anchor GJPPK2LBMQ" title="Click for about">Client: 5.1.1.6044</a>
<a class="gwt-Anchor GJPPK2LBMQ" title="Click for about">Engine: 5.1.1.5218</a>
My XPATH to locate the text Engine: 5.1.1
//a[contains(., "Engine: 5.1.1")]
Using the XPATH the text Engine: 5.1.1 is highlighted on the GUI
get_element is in my Base class. It's implementation is:
# returns the element if found
def get_element(self, how, what):
# params how: By locator type
# params what: locator value
try:
element = self.driver.find_element(by=how, value=what)
except NoSuchElementException, e:
print what
print "Element not found "
print e
screenshot_name = how + what + get_datetime_now() # create screenshot name of the name of the element + locator + todays date time. This way the screenshot name will be unique and be able to save
self.save_screenshot(screenshot_name)
raise
return element
Why is my element text value empty?
Thanks,
Riaz
I have encountered this problem when I tried to verify the population of text fields after a selection is made from a dropdown. As you mentioned, the texts are indeed there, but selenium thinks they are empty. If memory serves, it was because the form had not been submitted yet, and I believe the value attribute (not the text attribute) is where the getText() function actually pulls its data. The value attribute does not get set until later. Take a look at the elements' value attribute after you submit the page, and I believe those values will be set, and the getText() will return the correct text values. I was not able to find a work-around, since upon the user clicking the submit button, it was proceeding to the next web page.

How to make a plone view that inserts other smaller views of content items?

I think this should be simple. I have a folderish TTW dexterity content item (a drop box) that contains folderish TTW dexterity items (proposals). Each proposal contains TTW dexterity reviews that have fields I want to summarize.
I can easily make a view that generates a table as indicated below for any proposal with simple modifications to the folderlisting view:
[review1 link] [criterion_1 value] [criterion-2 value]...
[review2 link] [criterion_1 value] [criterion-2 value]...
.
.
I can also generate a working table view for a drop box by modifying the folderlisting view:
[proposal1 link] [column I would like to insert the above table in for this proposal]
[proposal2 link] [column I would like to insert the above table in for this proposal]
.
.
My problem is I cannot figure out how to insert the first table into the cells in the second column of the second table. I've tried two things:
Within the view template for the dropbox listing, I tried duplicating the repeat macro of the listingmacro, giving it and all its variables new names to have it iterate on each proposal. This easily accesses all of the Dublin core schemata for each review, but I cannot get access to the dexterity fields. Everything I have tried (things that work when generating the first table) yield LocationError and AttributeError warnings. Somehow when I go down one level I lose some of the information necessary for the view template to find everything. Any suggestions?
I've also tried accessing the listing macro for the proposal, with calls like <metal use-macro="item/first_table_template_name/listing"/>. Is this even partially the right approach? It gives no errors, but also does not insert anything into my page.
Thanks.
This solution is loosely based on the examples provided by kuel: https://github.com/plone/Products.CMFPlone/blob/854be6e30d1905a7bb0f20c66fbc1ba1f628eb1b/Products/CMFPlone/skins/plone_content/folder_full_view.pt and https://github.com/plone/Products.CMFPlone/blob/b94584e2b1231c44aa34dc2beb1ed9b0c9b9e5da/Products/CMFPlone/skins/plone_content/folder_full_view_item.pt. --Thank you.
The way I found easiest to create and debug this was:
Create a minimalist template from the plone standard template folder_listing.pt which makes just the table of summarized review data for a single proposal. The template is just for a table, no header info or any other slots. This is a stripped version, but there is nothing above the first statement. A key statement that allowed access to the fields were of the form:
python: item.getObject().restrictedTraverse('criterion_1')
The table template:
<table class="review_summary listing">
<tbody><tr class="column_labels"><th>Review</th><th>Scholarly Merit</th><th>Benefits to Student</th><th>Clarity</th><th>Sum</th></tr>
<metal:listingmacro define-macro="listing">
<tal:foldercontents define="contentFilter contentFilter|request/contentFilter|nothing;
contentFilter python:contentFilter and dict(contentFilter) or {};
I kept all the standard definitions from the original template.
I have just removed them for brevity.
plone_view context/##plone;">
The following tal:sum is where I did some math on my data. If you are
not manipulating the data this would not be needed. Note that I am only
looking at the first character of the choice field.
<tal:sum define="c1_list python:[int(temp.getObject().restrictedTraverse('criterion_1')[0])
for temp in batch if temp.portal_type=='ug_small_grants_review'];
c1_length python: test(len(c1_list)<1,-1,len(c1_list));
c2_list python:[int(temp.getObject().restrictedTraverse('criterion_2')[0])
for temp in batch if temp.portal_type=='ug_small_grants_review'];
c2_length python: test(len(c2_list)<1,-1,len(c2_list));
c1_avg python: round(float(sum(c1_list))/c1_length,2);
c2_avg python: round(float(sum(c2_list))/c2_length,2);
avg_sum python: c1_avg+c2_avg;
">
<tal:listing condition="batch">
<dl metal:define-slot="entries">
<tal:entry tal:repeat="item batch" metal:define-macro="entries">
<tal:block tal:define="item_url item/getURL|item/absolute_url;
item_id item/getId|item/id;
Again, this is the standard define from the folder_listing.pt
but I've left out most of it to save space here.
item_samedate python: (item_end - item_start < 1) if item_type == 'Event' else False;">
<metal:block define-slot="entry"
The following condition is key if you can have things
other than reviews within a proposal. Make sure the
item_type is proper for your review/item.
tal:condition="python: item_type=='ug_small_grants_review'">
<tr class="review_entry"><td class="entry_info">
<dt metal:define-macro="listitem"
tal:attributes="class python:test(item_type == 'Event', 'vevent', '')">
I kept all the standard stuff from folder_listing.pt here.
</dt>
<dd tal:condition="item_description">
</dd>
</td>
The following tal:comp block is used to calculate values
across the rows because we do not know the index of the
item the way the batch is iterated.
<tal:comp define = "crit_1 python: item.getObject().restrictedTraverse('criterion_1')[0];
crit_2 python: item.getObject().restrictedTraverse('criterion_2')[0];
">
<td tal:content="structure crit_1"># here</td>
<td tal:content="structure crit_2"># here</td>
<td tal:content="structure python: int(crit_1)+int(crit_2)"># here</td>
</tal:comp>
</tr>
</metal:block>
</tal:block>
</tal:entry>
</dl>
<tr>
<th>Average</th>
<td tal:content="structure c1_avg"># here</td>
<td tal:content="structure c2_avg"># here</td>
<td tal:content="structure avg_sum"># here</td>
</tr>
</tal:listing>
</tal:sum>
<metal:empty metal:define-slot="no_items_in_listing">
<p class="discreet"
tal:condition="not: folderContents"
i18n:translate="description_no_items_in_folder">
There are currently no items in this folder.
</p>
</metal:empty>
</tal:foldercontents>
</metal:listingmacro>
</tbody></table>
Create another listing template that calls this one to fill the appropriate table cell. Again, I used a modification of the folder_listing.pt. Basically within the repeat block I put the following statement in the second column of the table:
This belongs right after the </dd> tag ending the normal item listing.
</td> <td class="review_summary">
<div tal:replace="structure python:item.getObject().ug_small_grant_review_summary_table()" />
</td>
Note that "ug_small_grant_review_summary_table" is the name I gave to the template shown in more detail above.

jSoup - How to get elements with background style (inline CSS)?

I'm building an app in Railo, which uses the jSoup .jar library. It all works really well in my CFML language.
Anyhow, I can grab every element with a "style" attribute doing:
<cfset variables.mySelection = variables.myDocument.select("*[style]") />
But this returns an array which contains elements that sometimes do not have a "background" or "background-image" style on them. As an example, the HTML might looks like so:
<p style="color: red;">I should not be selected</p>
<p style="background: green">I **should** be selected</p>
<p style="text-align: left;">I should not be selected</p>
<p style="background-image: url("/path/to/image.jpg");">I **should** be selected</p>
So I can get these elements above, but I don't want the 1st and 3rd in my array, as they don't have a background style...do you know how I can only grab and work with these?
Please note, I'm not after a COMPUTATED style, or anything that complicated, I'm just wondering if I can filter based on the properties of an inline CSS style. Perhaps some regex after the fact? I'm open to ideas!
I tried messing with :contains(background) as a key word, but I wasn't sure if that was the correct path?
Many thanks for your help.
Michael.
Try with:
variables.myDocument.select("*[style*='background']")
As *= is the standard selector to match a substring in the attribute content.
Elements els = doc.select(div[style*=dashed]);
Or
Elements elements = doc1.select("span[style*=font-weight:bold]");