Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

src/google.py
src/chromedriver
src/chromedriver.exe
src/violations.txt

# --- Frontend
Expand Down
30 changes: 27 additions & 3 deletions src/frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,34 @@ The `ViolatingSitesTable.tsx` component requires an active API Key to retrieve a
8. Copy the newly created API key
9. Create a **_.env_** at **_./src/frontend/_** in your project
- The file should have the following contents:
```bash
REACT_APP_AD_EXPERIENCE_API_KEY="[API_KEY]"
```bash
REACT_APP_AD_EXPERIENCE_API_KEY=[API_KEY]
```
> Replace `[API_KEY]` with the key you copied in the previous step

## Controlling Site Filtering
By default, when you run the frontend, the sites that populate the main table of violating sites are the first 9 sites in the response received from Google's Ad Experience Report API. <br />

However, there is the option to filter out sites that do not exist. To do so, change the `FILTER_SITES` constant in **_.\src\frontend\src\components\ViolatingSitesTable.tsx_** from `false` to `true`.
> **NOTE**: Filtering out sites can take a long time (anywhere from a few minutes to tens of minutes, depending on network speed). This will impact the time it takes to populate the table of violating sites, both on initial page load, and every time a new list is requested.

## Controlling Port for the Backend
By default, port 8080 is used to run the Flask server. However, some situations may arise where you have to use a different port. The purpose of changing the port is to ensure the Flask server does not conflict with other applications/services and to ensure the frontend is using the correct port to communicate with the server. To change the port, follow these steps:
1. Change the port argument in the main function of **_./src/server.py_**
```python
# BEFORE
if __name__ == '__main__':
app.run(port=8080, debug=True)
```
```python
# AFTER
if __name__ == '__main__':
app.run(port=5000, debug=True)
```
> Replace `[API_KEY]` with the key you copied at step 8
2. Add a new environment variable to the **_./src/frontend/.env_** file. The variable will have your desired port number, like so:
```bash
REACT_APP_BACKEND_PORT=5000
```

## Documentation Reference

Expand Down
3 changes: 1 addition & 2 deletions src/frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"name": "frontend",
"version": "0.1.0",
"proxy": "http://localhost:8080",
"private": true,
"dependencies": {
"@types/node": "^16.18.93",
Expand Down Expand Up @@ -41,4 +40,4 @@
"devDependencies": {
"tailwindcss": "^3.4.3"
}
}
}
4 changes: 2 additions & 2 deletions src/frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ function App(): JSX.Element {
return (
<div
id="pageContainer"
className="bg-gradient-to-b dark:from-slate-900 dark:to-slate-950 from-slate-100 to-slate-200 pb-28 lg:pb-0"
className="bg-gradient-to-b dark:from-slate-900 dark:to-slate-950 from-slate-100 to-slate-200 pb-28 lg:pb-0 h-screen"
>
<div className="App">
<Header />
<div
id="pageBody"
className="flex flex-col flex-wrap justify-center items-center h-screen font-sans"
className="flex flex-col flex-wrap justify-center items-center h-full font-sans bg-gradient-to-b dark:from-slate-900 dark:to-slate-950 from-slate-100 to-slate-200 pb-28 lg:pb-0"
>
<ViolatingSitesTable />
</div>
Expand Down
84 changes: 84 additions & 0 deletions src/frontend/src/components/CustomURLTableRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { useState } from "react";
import FixSuggestions from "./FixSuggestion";

export default function ViolatingSiteTableRow() {
const BACKEND_PORT = process.env.REACT_APP_BACKEND_PORT || 8080; // Port where the backend server is running
const [loading, setLoading] = useState(false); // State to show/hide loading spinner
const [showViolations, setShowViolations] = useState(false); // State to show/hide violations
const [url, setUrl] = useState(""); // State to store the URL entered by the user

// Run AdHere.py against the url in this table row
const findViolations = async (site: string) => {
try {
setLoading(true);

// Pass url to endpoint that runs AdHere
const response = await fetch(
// If 'options object' issues are encountered then delete the following line from package.json
// "proxy": "http://localhost:8080",
`http://localhost:${BACKEND_PORT}/find-violations?url=${encodeURIComponent(site)}`,
{
method: "GET",
}
);

// AdHere.py execution complete
if (!response.ok) {
throw new Error("Failed to fetch data");
} else {
setShowViolations(true); // Trigger component to show violations
}
} catch (error) {
console.error("Error running AdHere on custom url ", site, " : ", error);
} finally {
setLoading(false);
}
};

return (
<div

className={`flex flex-row flex-wrap justify-between items-center w-full bg-slate-100 dark:bg-slate-800 p-2 border-b border-x border-slate-300 dark:border-slate-700${!showViolations ? " hover:bg-slate-300 dark:hover:bg-slate-600" : ""
}`}
>
{/* Text box */}
<input className="bg-slate-150 dark:bg-slate-700 text-black dark:text-white placeholder:italic placeholder:text-slate-200 placeholder:dark:text-slate-500 rounded-md p-2"
placeholder="ex. violatingsite.com"
type="text"
onChange={(e) => setUrl(e.target.value.trim())}>
</input>

{/* Row Button */}
{/* Default: Run AdHere || Waiting on AdHere execution: circle_spin || Showing violations: Close */}
{!showViolations ? (
loading ? (
<div className="bg-slate-100 dark:bg-slate-800 p-2">
<img
src="./circle_spin.svg"
alt="loading spinner"
height={25}
width={25}
/>
</div>
) : (
<button
onClick={() => findViolations(url)}
className="bg-green-600 hover:bg-green-600 bg-opacity-20 text-green-500 hover:text-white p-2 rounded-md"
>
Run AdHere
</button>
)
) : (
<button
onClick={() => setShowViolations(false)}
className="bg-red-600 hover:bg-red-600 bg-opacity-20 text-red-500 hover:text-white p-2 rounded-md"
>
Close
</button>
)}

{/* Render component to show violations only when AdHere completes execution */}
{showViolations && <FixSuggestions />}
</div>
);
}
7 changes: 5 additions & 2 deletions src/frontend/src/components/FixSuggestion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import CodeEditor from "@uiw/react-textarea-code-editor";

export default function FixSuggestions() {
const [fixText, setFixText] = useState("");
const BACKEND_PORT = process.env.REACT_APP_BACKEND_PORT || 8080; // Port where the backend server is running

useEffect(() => {
// Fetch the fix suggestions from the Flask server
const fetchData = async () => {
try {
const response = await fetch("/get-violations");
const response = await fetch(`http://localhost:${BACKEND_PORT}/get-violations`);
const data = await response.text();
setFixText(data);
} catch (error) {
Expand All @@ -26,7 +28,8 @@ export default function FixSuggestions() {
language="js"
data-color-mode={localStorage.theme}
style={{
overflow: "scroll",
overflowY: "scroll",
overflowX: "hidden",
height: "20rem",
fontSize: "1rem",
}}
Expand Down
6 changes: 3 additions & 3 deletions src/frontend/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default function Header() {
const [pageTheme, setPageTheme] = useState("dark");

// Element where 'dark' class will be applied
const app = document.documentElement.getElementsByTagName("div");
const divs = document.documentElement.getElementsByTagName("div");
const iconStyling =
"text-zinc-700 dark:text-white hover:text-slate-400 cursor-pointer";

Expand All @@ -26,10 +26,10 @@ export default function Header() {
// toggle between dark and light theme
useEffect(() => {
if (pageTheme === "dark") {
app[0].classList.add("dark");
divs[0].classList.add("dark");
localStorage.theme = "dark";
} else {
app[0].classList.remove("dark");
divs[0].classList.remove("dark");
localStorage.theme = "light";
}
// eslint-disable-next-line react-hooks/exhaustive-deps
Expand Down
29 changes: 14 additions & 15 deletions src/frontend/src/components/ViolatingSiteTableRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,20 @@ interface RowProps {
}

export default function ViolatingSiteTableRow(props: RowProps) {
const [loading, setLoading] = useState(false);
const [showViolations, setShowViolations] = useState(false);
const BACKEND_PORT = process.env.REACT_APP_BACKEND_PORT || 8080; // Port where the backend server is running
const [loading, setLoading] = useState(false); // State to show/hide loading spinner
const [showViolations, setShowViolations] = useState(false); // State to show/hide violations

// Run AdHere.py against the url in this table row
const findViolations = async (site: string, index: number) => {
const findViolations = async (site: string) => {
try {
setLoading(true);

// Pass url to endpoint that runs AdHere
const response = await fetch(
// If 'options object' issues are encountered then delete the following line from package.json
// "proxy": "http://localhost:8080",
`http://localhost:8080/find-violations?url=${encodeURIComponent(site)}`,
`http://localhost:${BACKEND_PORT}/find-violations?url=${encodeURIComponent(site)}`,
{
method: "GET",
}
Expand All @@ -29,7 +30,7 @@ export default function ViolatingSiteTableRow(props: RowProps) {
if (!response.ok) {
throw new Error("Failed to fetch data");
} else {
setShowViolations(true); // Trigger component @1
setShowViolations(true); // Trigger component to show violations
}
} catch (error) {
console.error("Error running AdHere on ", site, ": ", error);
Expand All @@ -41,17 +42,15 @@ export default function ViolatingSiteTableRow(props: RowProps) {
return (
<div
key={props.key}
className={`flex flex-row flex-wrap justify-between items-center w-full bg-slate-100 dark:bg-slate-800 p-2 border-b border-x border-slate-300 dark:border-slate-700${
!showViolations ? " hover:bg-slate-300 dark:hover:bg-slate-600" : ""
}`}
className={`flex flex-row flex-wrap justify-between items-center w-full bg-slate-100 dark:bg-slate-800 p-2 border-b border-x border-slate-300 dark:border-slate-700${!showViolations ? " hover:bg-slate-300 dark:hover:bg-slate-600" : ""
}`}
>
{/* Site url */}
<p
className={`text-xl ${
!showViolations
? "text-gray-400 dark:text-gray-500"
: "text-xl text-black dark:text-white"
}`}
className={`text-xl ${!showViolations
? "text-gray-400 dark:text-gray-500"
: "text-xl text-black dark:text-white"
}`}
>
{props.url}
</p>
Expand All @@ -70,7 +69,7 @@ export default function ViolatingSiteTableRow(props: RowProps) {
</div>
) : (
<button
onClick={() => findViolations(props.url, props.key)}
onClick={() => findViolations(props.url)}
className="bg-green-600 hover:bg-green-600 bg-opacity-20 text-green-500 hover:text-white p-2 rounded-md"
>
Run AdHere
Expand All @@ -85,7 +84,7 @@ export default function ViolatingSiteTableRow(props: RowProps) {
</button>
)}

{/* @1 Render component to show violations only when AdHere completes execution */}
{/* Render component to show violations only when AdHere completes execution */}
{showViolations && <FixSuggestions />}
</div>
);
Expand Down
58 changes: 36 additions & 22 deletions src/frontend/src/components/ViolatingSitesTable.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { useEffect, useState } from "react";
import ViolatingSiteTableRow from "./ViolatingSiteTableRow";
import CustomURLTableRow from "./CustomURLTableRow";

export default function ViolatingSitesTable() {
const FILTER_SITES = false;
const BACKEND_PORT = process.env.REACT_APP_BACKEND_PORT || 8080; // Port where the backend server is running
const [loading, setLoading] = useState(false);
const [violatingSites, setViolatingSites] = useState<string[]>([]);

const numSitesToShow = 10;
const numSitesToShow = 9;

// Get list of violating sites
const getNewSites = async () => {
Expand All @@ -32,22 +35,29 @@ export default function ViolatingSitesTable() {
violatingSitesIndex < data.violatingSites.length
) {
const site = data.violatingSites[violatingSitesIndex++].reviewedSite;
// Calls Flask server endpoint which returns true or false depending on whether or not the site exists
await fetch(
`http://localhost:8080/url-check?url=${encodeURIComponent(site)}`
)
.then((response) => response.json())
.then((data) => {
if (
data.does_url_exist &&
filteredSites.length !== numSitesToShow
) {
filteredSites.push(site);
}
})
.catch((error) => {
console.error("Error:", error);
});

if (FILTER_SITES) {
// Calls Flask server endpoint which returns true or false depending on whether or not the site exists
await fetch(
`http://localhost:${BACKEND_PORT}/url-check?url=${encodeURIComponent(site)}`
)
.then((response) => response.json())
.then((data) => {
if (
data.does_url_exist &&
filteredSites.length !== numSitesToShow
) {
filteredSites.push(site);
}
})
.catch((error) => {
console.error("Error:", error);
});
}
else {
// If we're not filtering sites, just add them to the list
filteredSites.push(site);
}
}

// Update state with the new sites
Expand All @@ -65,7 +75,7 @@ export default function ViolatingSitesTable() {
}, []);

return (
<div className="flex flex-col justify-center items-start w-full sm:w-8/12 lg:w-1/2 sm:px-10 lg:px-20 mt-28 md:mt-0">
<div className="flex flex-col justify-center items-start w-full sm:w-8/12 lg:w-1/2 sm:px-10 lg:px-20 py-5 md:mt-0">
{/* Row plus button for getting new sites */}
<div className="flex flex-row justify-start items-center w-full pb-2 border-b border-slate-300 dark:border-slate-700">
<button
Expand All @@ -82,10 +92,14 @@ export default function ViolatingSitesTable() {
<img src="./circle_spin.svg" alt="loading spinner" />
</div>
) : (
// Map over sites and display them
violatingSites.map((site, index) => (
<ViolatingSiteTableRow key={index} url={site} />
))
<div className="w-full">
<CustomURLTableRow />
{// Map over sites and display them
violatingSites.map((site, index) => (
<ViolatingSiteTableRow key={index} url={site} />
))}

</div>
)}
</div>
);
Expand Down