Skip to content

Commit

Permalink
Merge pull request #3 from fpgmaas/aks
Browse files Browse the repository at this point in the history
Add support to deploy on Azure
  • Loading branch information
fpgmaas authored Jun 22, 2024
2 parents 52e06d3 + 36ddac4 commit 0287408
Show file tree
Hide file tree
Showing 32 changed files with 673 additions and 99 deletions.
Binary file added .DS_Store
Binary file not shown.
20 changes: 19 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
workflow_dispatch:

jobs:
push-to-acr:
build-and-push-backend:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
Expand All @@ -29,6 +29,22 @@ jobs:
push: true
tags: pypiscoutacr.azurecr.io/pypi-scout-backend:latest

build-and-push-frontend:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Login to Azure Container Registry
uses: azure/docker-login@v1
with:
login-server: pypiscoutacr.azurecr.io
username: ${{ secrets.ACR_USERNAME }}
password: ${{ secrets.ACR_PASSWORD }}

- name: Build and Push Frontend Docker image
uses: docker/build-push-action@v4
with:
Expand All @@ -37,3 +53,5 @@ jobs:
platforms: linux/amd64
push: true
tags: pypiscoutacr.azurecr.io/pypi-scout-frontend:latest
build-args: |
NEXT_PUBLIC_API_URL=https://pypiscout.com/api
6 changes: 1 addition & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,10 @@ RUN poetry install --no-interaction --no-ansi --no-root --no-dev && \
# Copy Python code to the Docker image
COPY pypi_scout /code/pypi_scout/

# Copy the start script and make executable
COPY start.sh /start.sh
RUN chmod +x /start.sh

# Make empty data directory
RUN mkdir -p /code/data

ENV PYTHONPATH=/code

# Use the script as the entrypoint
ENTRYPOINT ["/start.sh"]
CMD ["uvicorn", "pypi_scout.api.main:app", "--host", "0.0.0.0", "--port", "8000"]
6 changes: 1 addition & 5 deletions DockerfileCPU
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,10 @@ RUN pip install --no-cache-dir -r requirements-cpu.txt
# Copy the rest of the application code
COPY pypi_scout /code/pypi_scout/

# Copy the start script and make it executable
COPY start.sh /start.sh
RUN chmod +x /start.sh

# Make empty data directory
RUN mkdir -p /code/data

ENV PYTHONPATH=/code

# Use the script as the entrypoint
ENTRYPOINT ["/start.sh"]
CMD ["uvicorn", "pypi_scout.api.main:app", "--host", "0.0.0.0", "--port", "8000"]
8 changes: 3 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,20 @@ services:
build:
context: .
dockerfile: Dockerfile
working_dir: /
command: uvicorn pypi_scout.api.main:app --host 0.0.0.0 --port 8000
ports:
- "8000:8000"
volumes:
- ./data:/data
- ./data:/code/data
env_file:
- .env

frontend:
build:
context: ./frontend
dockerfile: Dockerfile
args:
NEXT_PUBLIC_API_URL: http://localhost:8000/api
ports:
- "3000:3000"
environment:
- NODE_ENV=production
depends_on:
- backend
5 changes: 5 additions & 0 deletions frontend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# .dockerignore
node_modules
.next
.env
.git
6 changes: 6 additions & 0 deletions frontend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ RUN npm install
# Copy the rest of the application code to the container
COPY . .

# Build argument to accept the API URL during build time
ARG NEXT_PUBLIC_API_URL

# Set environment variable within the container
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}

# Build the Next.js application
RUN npm run build

Expand Down
25 changes: 25 additions & 0 deletions frontend/app/components/GitHubButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from "react";

const GitHubButton: React.FC = () => {
return (
<a
href="https://github.com/fpgmaas/pypi-scout"
target="_blank"
rel="noopener noreferrer"
className="flex items-center p-2 border border-gray-700 rounded bg-gray-900 text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-700"
>
<svg
height="24"
width="24"
viewBox="0 0 16 16"
fill="white"
className="mr-2"
>
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.58.82-2.14-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.14 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.45.55.38A8.001 8.001 0 0 0 16 8c0-4.42-3.58-8-8-8z" />
</svg>
GitHub
</a>
);
};

export default GitHubButton;
13 changes: 7 additions & 6 deletions frontend/app/components/InfoBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,17 @@ const InfoBox: React.FC<InfoBoxProps> = ({ infoBoxVisible }) => {
<h2 className="text-2xl font-bold mb-2">How does this work?</h2>
<p className="text-gray-300">
This application allows you to search for Python packages on PyPI using
natural language. An example query would be &quot;a package that creates
plots and beautiful visualizations&quot;.
natural language queries. For example, a query could be &quot;a package
that creates plots and beautiful visualizations&quot;.
</p>
<br />
<p className="text-gray-300">
Once you click search, your query will be matched against the summary
and the first part of the description of all PyPI packages with more
than 50 weekly downloads. The results are then scored based on their
similarity and their number of weekly downloads, and the best results
are displayed in the table below.
and the first part of the description of the ~30.000 most popular
packages on PyPI, which are all packages with at least ~600 downloads
per week. The results are then scored based on their similarity to the
query and their number of weekly downloads, and the best results are
displayed in the table below.
</p>
</div>
);
Expand Down
10 changes: 8 additions & 2 deletions frontend/app/components/SearchResultsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ const SearchResultsTable: React.FC<SearchResultsTableProps> = ({
return sortField === field ? (sortDirection === "asc" ? "▲" : "▼") : "";
};

const truncateText = (text: string, maxLength: number) => {
return text.length > maxLength
? `${text.substring(0, maxLength)}...`
: text;
};

return (
<div className="overflow-x-auto w-full">
<table className="min-w-full divide-y divide-gray-700">
Expand Down Expand Up @@ -70,15 +76,15 @@ const SearchResultsTable: React.FC<SearchResultsTableProps> = ({
{results.map((result, index) => (
<tr key={index} className="hover:bg-gray-700">
<td className="px-4 py-2 whitespace-nowrap text-gray-200">
{result.name}
{truncateText(result.name, 20)}
</td>
<td className="px-4 py-2 whitespace-nowrap text-gray-200">
{result.similarity.toFixed(3)}
</td>
<td className="px-4 py-2 whitespace-nowrap text-gray-200">
{result.weekly_downloads.toLocaleString()}
</td>
<td className="px-4 py-2 whitespace-normal break-words text-gray-200">
<td className="px-4 py-2 whitespace-normal break-words text-gray-200 min-w-[400px]">
{result.summary}
</td>
<td className="px-4 py-2 whitespace-nowrap">
Expand Down
23 changes: 23 additions & 0 deletions frontend/app/components/SupportButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from "react";

const SupportButton: React.FC = () => {
return (
<a
href="https://ko-fi.com/fpgmaas"
target="_blank"
rel="noopener noreferrer"
className="flex items-center p-2 border border-gray-700 rounded bg-gray-900 text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-700"
>
<img
src="kofi.png"
alt="Ko-fi logo"
width="24"
height="24"
className="mr-2"
/>
Support
</a>
);
};

export default SupportButton;
11 changes: 3 additions & 8 deletions frontend/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
--dark-bg-start-rgb: 10, 10, 35; /* Very dark blue almost grey */
--dark-bg-end-rgb: 25, 25, 50; /* Dark blue */
--dark-bg-start-rgb: 17, 24, 39; /* Dark gray (bg-gray-900) */
--dark-bg-end-rgb: 17, 24, 39; /* Dark gray (bg-gray-900) */
--dark-foreground-rgb: 255, 255, 255;
}

Expand All @@ -21,12 +21,7 @@

body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
background: rgb(var(--background-start-rgb)); /* Solid background color */
}

@layer utilities {
Expand Down
21 changes: 16 additions & 5 deletions frontend/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { handleSearch, sortResults } from "./utils/search";
import SearchResultsTable from "./components/SearchResultsTable";
import InfoBox from "./components/InfoBox";
import { ClipLoader } from "react-spinners";
import GitHubButton from "./components/GitHubButton";
import SupportButton from "./components/SupportButton";

interface Match {
name: string;
Expand All @@ -31,20 +33,29 @@ export default function Home() {
};

return (
<main className="flex flex-col items-center justify-start min-h-screen p-4 space-y-4 bg-gray-900">
<header className="w-full flex flex-col items-center text-center mb-4">
<main className="flex flex-col items-center justify-start min-h-screen p-4 space-y-4 max-w-[2200px] mx-auto relative">
<header className="w-full flex justify-end p-4">
<div className="flex space-x-4">
<GitHubButton />
<SupportButton />
</div>
</header>

<div className="flex flex-col items-center text-center mb-4">
<picture>
<img
alt="pypi-scout logo"
width="420"
height="220"
src="./pypi.svg"
className="mx-auto"
></img>
</picture>
<p className="text-lg text-gray-300 mt-2">
Enter your query to search for Python packages
</p>
</header>
</div>

<div className="flex flex-col items-center space-y-4 w-3/5 bg-gray-800 p-6 rounded-lg shadow-lg">
<textarea
className="w-full h-24 p-2 border border-gray-700 rounded resize-none overflow-auto focus:outline-none focus:ring-2 focus:ring-blue-700 bg-gray-700 text-white"
Expand All @@ -53,7 +64,7 @@ export default function Home() {
placeholder="Enter your query here..."
></textarea>
<button
className="w-[250px] p-2 border border-gray-700 rounded bg-blue-600 text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-700"
className="w-full max-w-[250px] p-2 border border-gray-700 rounded bg-blue-600 text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-700"
onClick={() =>
handleSearch(
text,
Expand All @@ -75,7 +86,7 @@ export default function Home() {

<div className="w-full flex justify-center mt-6">
<button
className="w-[250px] p-2 border border-gray-700 rounded bg-gray-600 text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-700"
className="w-full max-w-[250px] p-2 border border-gray-700 rounded bg-gray-600 text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-gray-700"
onClick={() => setInfoBoxVisible(!infoBoxVisible)}
>
{infoBoxVisible ? "Hide Info" : "How does this work?"}
Expand Down
33 changes: 26 additions & 7 deletions frontend/app/utils/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ interface Match {
summary: string;
}

interface SearchResponse {
matches: Match[];
warning?: boolean;
warning_message?: string;
}

const apiUrl = process.env.NEXT_PUBLIC_API_URL;

export const handleSearch = async (
query: string,
sortField: string,
Expand All @@ -18,8 +26,8 @@ export const handleSearch = async (
setLoading(true);
setError("");
try {
const response = await axios.post(
"http://localhost:8000/search",
const response = await axios.post<SearchResponse>(
`${apiUrl}/search`,
{
query: query,
},
Expand All @@ -29,10 +37,20 @@ export const handleSearch = async (
},
},
);
const fetchedResults: Match[] = response.data.matches;
setResults(sortResults(fetchedResults, sortField, sortDirection));

const { matches, warning, warning_message } = response.data;

if (warning && warning_message) {
console.warn("Warning from API:", warning_message);
}

setResults(sortResults(matches, sortField, sortDirection));
} catch (error) {
setError("Error fetching search results.");
if (axios.isAxiosError(error) && error.response?.status === 429) {
setError("Rate limit reached. Please wait a minute and try again.");
} else {
setError("Error fetching search results.");
}
console.error("Error fetching search results:", error);
} finally {
setLoading(false);
Expand All @@ -46,8 +64,9 @@ export const sortResults = (
): Match[] => {
return [...data].sort((a, b) => {
// @ts-ignore
if (a[field] < b[field]) return direction === "asc" ? -1 : 1; // @ts-ignore
if (a[field] > b[field]) return direction === "asc" ? 1 : -1; // @ts-ignore
if (a[field] < b[field]) return direction === "asc" ? -1 : 1;
// @ts-ignore
if (a[field] > b[field]) return direction === "asc" ? 1 : -1;
return 0;
});
};
7 changes: 6 additions & 1 deletion frontend/next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
const nextConfig = {
env: {
NEXT_PUBLIC_API_URL:
process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000/api",
},
};

export default nextConfig;
Binary file added frontend/public/kofi.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 0287408

Please sign in to comment.