Spaces:
Sleeping
Sleeping
Commit
·
259b34f
1
Parent(s):
ef730bf
Add application file
Browse files- Dockerfile +25 -0
- README.md +1 -1
- app.py +61 -0
- requirements.txt +7 -0
- templates/index.html +107 -0
Dockerfile
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.9-slim
|
2 |
+
|
3 |
+
WORKDIR /code
|
4 |
+
|
5 |
+
# Install system dependencies
|
6 |
+
RUN apt-get update && apt-get install -y \
|
7 |
+
build-essential \
|
8 |
+
&& rm -rf /var/lib/apt/lists/*
|
9 |
+
|
10 |
+
# Create necessary directories
|
11 |
+
RUN mkdir -p /code/templates
|
12 |
+
|
13 |
+
# Copy requirements first to leverage Docker cache
|
14 |
+
COPY ./requirements.txt /code/requirements.txt
|
15 |
+
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
16 |
+
|
17 |
+
# Copy application code
|
18 |
+
COPY ./app.py /code/app.py
|
19 |
+
COPY ./templates/* /code/templates/
|
20 |
+
|
21 |
+
# Make port 7860 available to the world outside this container
|
22 |
+
EXPOSE 7860
|
23 |
+
|
24 |
+
# Run gunicorn
|
25 |
+
CMD ["gunicorn", "--bind", "0.0.0.0:7860", "app:app", "--timeout", "300"]
|
README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
---
|
2 |
title: PatentExplorerApp
|
3 |
-
emoji:
|
4 |
colorFrom: blue
|
5 |
colorTo: gray
|
6 |
sdk: docker
|
|
|
1 |
---
|
2 |
title: PatentExplorerApp
|
3 |
+
emoji: 💡
|
4 |
colorFrom: blue
|
5 |
colorTo: gray
|
6 |
sdk: docker
|
app.py
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import Flask, render_template, request, jsonify
|
2 |
+
from serpapi import GoogleSearch
|
3 |
+
from datetime import datetime
|
4 |
+
|
5 |
+
app = Flask(__name__)
|
6 |
+
|
7 |
+
def search_patents(keywords, num_results=15):
|
8 |
+
"""
|
9 |
+
Search patents using SerpAPI Google Patents
|
10 |
+
"""
|
11 |
+
api_key = "1c3072feed5fcd1cdc029bbb7f27a87c7e7aac5710cf05dcd5960dfeebeb8fec"
|
12 |
+
search_params = {
|
13 |
+
"engine": "google_patents",
|
14 |
+
"q": keywords,
|
15 |
+
"api_key": api_key,
|
16 |
+
"num": num_results
|
17 |
+
}
|
18 |
+
|
19 |
+
try:
|
20 |
+
search = GoogleSearch(search_params)
|
21 |
+
patents = search.get_dict().get("organic_results", [])
|
22 |
+
|
23 |
+
formatted_patents = []
|
24 |
+
for patent in patents:
|
25 |
+
# Format filing date
|
26 |
+
date_str = patent.get('filing_date', '')
|
27 |
+
filing_year = 'N/A'
|
28 |
+
if date_str:
|
29 |
+
try:
|
30 |
+
filing_year = datetime.strptime(date_str, '%Y-%m-%d').year
|
31 |
+
except ValueError:
|
32 |
+
pass
|
33 |
+
|
34 |
+
formatted_patent = {
|
35 |
+
'title': patent.get('title', 'N/A'),
|
36 |
+
'assignee': patent.get('assignee', 'N/A'),
|
37 |
+
'filing_year': filing_year,
|
38 |
+
'abstract': patent.get('snippet', 'N/A'),
|
39 |
+
'link': patent.get('patent_link', '#')
|
40 |
+
}
|
41 |
+
formatted_patents.append(formatted_patent)
|
42 |
+
|
43 |
+
return formatted_patents
|
44 |
+
except Exception as e:
|
45 |
+
return []
|
46 |
+
|
47 |
+
@app.route('/')
|
48 |
+
def home():
|
49 |
+
return render_template('index.html')
|
50 |
+
|
51 |
+
@app.route('/search', methods=['POST'])
|
52 |
+
def search():
|
53 |
+
keywords = request.form.get('keywords', '')
|
54 |
+
if not keywords:
|
55 |
+
return jsonify({'error': 'Please enter search keywords'})
|
56 |
+
|
57 |
+
patents = search_patents(keywords)
|
58 |
+
return jsonify({'patents': patents})
|
59 |
+
|
60 |
+
if __name__ == '__main__':
|
61 |
+
app.run(host='0.0.0.0', port=7860)
|
requirements.txt
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
flask==2.0.1
|
2 |
+
Werkzeug==2.0.3
|
3 |
+
google-search-results==2.4.2
|
4 |
+
gunicorn==20.1.0
|
5 |
+
itsdangerous==2.0.1
|
6 |
+
Jinja2==3.0.1
|
7 |
+
MarkupSafe==2.0.1
|
templates/index.html
ADDED
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>Patent Explorer</title>
|
7 |
+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
|
8 |
+
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
9 |
+
</head>
|
10 |
+
<body class="bg-gray-100 min-h-screen">
|
11 |
+
<div class="container mx-auto px-4 py-8">
|
12 |
+
<h1 class="text-4xl font-bold text-center text-blue-600 mb-8">Patent Explorer</h1>
|
13 |
+
|
14 |
+
<!-- Search Form -->
|
15 |
+
<div class="max-w-2xl mx-auto mb-8">
|
16 |
+
<form id="searchForm" class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
|
17 |
+
<div class="mb-4">
|
18 |
+
<input type="text" id="keywords" name="keywords"
|
19 |
+
class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
|
20 |
+
placeholder="Enter keywords to search patents...">
|
21 |
+
</div>
|
22 |
+
<div class="flex items-center justify-center">
|
23 |
+
<button type="submit"
|
24 |
+
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline">
|
25 |
+
Search Patents
|
26 |
+
</button>
|
27 |
+
</div>
|
28 |
+
</form>
|
29 |
+
</div>
|
30 |
+
|
31 |
+
<!-- Loading Spinner -->
|
32 |
+
<div id="loading" class="hidden">
|
33 |
+
<div class="flex justify-center items-center">
|
34 |
+
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500"></div>
|
35 |
+
</div>
|
36 |
+
</div>
|
37 |
+
|
38 |
+
<!-- Results Container -->
|
39 |
+
<div id="results" class="max-w-4xl mx-auto"></div>
|
40 |
+
</div>
|
41 |
+
|
42 |
+
<script>
|
43 |
+
$(document).ready(function() {
|
44 |
+
$('#searchForm').on('submit', function(e) {
|
45 |
+
e.preventDefault();
|
46 |
+
const keywords = $('#keywords').val();
|
47 |
+
|
48 |
+
if (!keywords) {
|
49 |
+
alert('Please enter search keywords');
|
50 |
+
return;
|
51 |
+
}
|
52 |
+
|
53 |
+
// Show loading spinner
|
54 |
+
$('#loading').removeClass('hidden');
|
55 |
+
$('#results').empty();
|
56 |
+
|
57 |
+
$.ajax({
|
58 |
+
url: '/search',
|
59 |
+
method: 'POST',
|
60 |
+
data: { keywords: keywords },
|
61 |
+
success: function(response) {
|
62 |
+
$('#loading').addClass('hidden');
|
63 |
+
|
64 |
+
if (response.error) {
|
65 |
+
$('#results').html(`<div class="text-red-500 text-center">${response.error}</div>`);
|
66 |
+
return;
|
67 |
+
}
|
68 |
+
|
69 |
+
if (!response.patents.length) {
|
70 |
+
$('#results').html('<div class="text-center text-gray-600">No patents found.</div>');
|
71 |
+
return;
|
72 |
+
}
|
73 |
+
|
74 |
+
const resultsHtml = response.patents.map((patent, index) => `
|
75 |
+
<div class="bg-white shadow-md rounded-lg p-6 mb-4">
|
76 |
+
<h2 class="text-xl font-bold text-blue-600 mb-2">
|
77 |
+
<a href="${patent.link}" target="_blank" class="hover:underline">
|
78 |
+
${patent.title}
|
79 |
+
</a>
|
80 |
+
</h2>
|
81 |
+
<div class="grid grid-cols-2 gap-4 mb-4 text-sm">
|
82 |
+
<div>
|
83 |
+
<span class="font-semibold">Assignee:</span> ${patent.assignee}
|
84 |
+
</div>
|
85 |
+
<div>
|
86 |
+
<span class="font-semibold">Filing Year:</span> ${patent.filing_year}
|
87 |
+
</div>
|
88 |
+
</div>
|
89 |
+
<div class="text-gray-600">
|
90 |
+
<span class="font-semibold">Abstract:</span><br>
|
91 |
+
${patent.abstract}
|
92 |
+
</div>
|
93 |
+
</div>
|
94 |
+
`).join('');
|
95 |
+
|
96 |
+
$('#results').html(resultsHtml);
|
97 |
+
},
|
98 |
+
error: function() {
|
99 |
+
$('#loading').addClass('hidden');
|
100 |
+
$('#results').html('<div class="text-red-500 text-center">An error occurred while searching patents.</div>');
|
101 |
+
}
|
102 |
+
});
|
103 |
+
});
|
104 |
+
});
|
105 |
+
</script>
|
106 |
+
</body>
|
107 |
+
</html>
|