Skip to content

Commit

Permalink
polish up explore a bit
Browse files Browse the repository at this point in the history
  • Loading branch information
ryaanahmed committed Sep 14, 2023
1 parent dd5486f commit 208ecaf
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 93 deletions.
49 changes: 34 additions & 15 deletions backend/app/api_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from rest_framework.decorators import api_view
from rest_framework.response import Response

from django.db.models import Q
from django.db.models import Q, Prefetch
from django.core.paginator import Paginator

from app.view_helpers import (
Expand Down Expand Up @@ -235,7 +235,8 @@ def parse_order_by(order_by):

serialized_photographers = (
PhotographerSearchSerializer(current_page.object_list, many=True)
) # add pagination here
)
# add pagination here
res = Response({
"page_number": page_number,
"results": serialized_photographers.data,
Expand Down Expand Up @@ -398,8 +399,8 @@ def get_random_photos(request):

photos = list(Photo.objects.all())
print(photos)
random_photos = random.sample(photos,9)
serializer = SimplePhotoSerializerForCollage(random_photos,many=True)
random_photos = random.sample(photos, 9)
serializer = SimplePhotoSerializerForCollage(random_photos, many=True)
return Response(serializer.data)

@api_view(['GET'])
Expand Down Expand Up @@ -475,7 +476,9 @@ def explore(request):
to all of the photos in the collection
"""
tag = request.data.get('selectedTag')
map_square = request.data.get('selectedMapSquare')
page = int(request.data.get('page', 1))
page_size = int(request.data.get('pageSize', 10))

ALL = 'All'

query = Q()
Expand All @@ -484,20 +487,36 @@ def explore(request):
if tag != ALL:
query |= Q(analyses__name='yolo_model', analyses__result__icontains=tag)

# Filter by map squares if provided
if map_square != ALL:
query |= Q(map_square__number=int(map_square))

photos = Photo.objects.filter(query).distinct()
if tag != ALL:
photos = sorted(photos, key=lambda photo: tag_confidence(photo, tag))
prefetch = Prefetch('analyses', queryset=PhotoAnalysisResult.objects.filter(name='yolo_model'))
photos = Photo.objects.filter(query).prefetch_related(prefetch).distinct()
photos_with_analysis = [
{
'photo': photo,
'analysis_result': list(photo.analyses.all())[0] if photo.analyses.all() else None
}
for photo in photos]

photos_with_analysis.sort(key=lambda item: tag_confidence(item['photo'], item['analysis_result'], tag))

# Extract sorted photos from the photos_with_analysis list
photos = [item['photo'] for item in photos_with_analysis]

else:
photos = Photo.objects.filter(query).distinct()

start_idx = (page - 1) * page_size
end_idx = start_idx + page_size
paginated_photos = photos[start_idx:end_idx]
total_pages = -(-len(photos) // page_size) # This is a way to do ceiling division in Python

# Serialize and return
photo_serializer = SimplePhotoSerializer(photos[:10], many=True)
print(photo_serializer.data)
photo_serializer = SimplePhotoSerializer(paginated_photos, many=True)

return Response({
'photos': photo_serializer.data
'photos': photo_serializer.data,
'currentPage': page,
'totalPages': total_pages,
'totalCount': len(photos),
})


Expand Down
8 changes: 4 additions & 4 deletions backend/app/management/commands/sync_photo_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,14 @@ def handle(self, *args, **options):
metadata = PhotoMetadata(map_square_number=int(map_square_number),
folder_number=int(folder_number),
photo_number=int(photo_number,),
full_text=full_text,
full_text=full_text,
photographer_number=photographer_number,
street_name=street_name,
street_name=street_name,
public_notes=public_notes,
private_notes=private_notes)
photo_metadata_list.append(metadata)
for metadata in photo_metadata_list:

for metadata in photo_metadata_list:
photo_number = metadata.photo_number
map_square_number = metadata.map_square_number
folder_number = metadata.folder_number
Expand Down
14 changes: 2 additions & 12 deletions backend/app/view_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,9 @@ def get_all_yolo_tags():
return out


def tag_confidence(photo_obj, tag):
def tag_confidence(photo_obj, analysis_result, tag):
""" given a photo object and a tag, get the maximum confidence of that tag"""
try:
analysis_result = PhotoAnalysisResult.objects.get(name='yolo_model', photo=photo_obj)
except PhotoAnalysisResult.DoesNotExist:
if not analysis_result:
return 100

yolo_dict = analysis_result.parsed_result()
Expand All @@ -119,11 +117,3 @@ def tag_confidence(photo_obj, tag):
default=100
)
return confidence








1 change: 0 additions & 1 deletion backend/config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
],

}

CORS_ORIGIN_WHITELIST = [
Expand Down
14 changes: 7 additions & 7 deletions backend/config/settings/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@
},
},
# Comment me in to get database query logging in the console.
# 'loggers': {
# 'django.db.backends': {
# 'level': 'DEBUG',
# 'handlers': ['console_db'],
# 'propagate': False,
# },
# },
'loggers': {
'django.db.backends': {
'level': 'DEBUG',
'handlers': ['console_db'],
'propagate': False,
},
},
}
16 changes: 2 additions & 14 deletions backend/templates/includes/navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,14 @@
<a class="nav-link" href="/map/">Map</a>
</li>
<li>
<a class="nav-link" href="/photographers/">Photographers</a>
<a class="nav-link" href="/explore/">Explore</a>
</li>
<li>
<a class="nav-link" href="/search/">Search</a>
<a class="nav-link" href="/blog/">Articles</a>
</li>
<li>
<a class="nav-link" href="/about/">About</a>
</li>
<li>
<a class="nav-link" href="/blog/">Blog</a>
</li>
{% if user.is_authenticated %}
<li>
<a class="nav-link" href="/logout/">Logout</a>
</li>
{% else %}
<li>
<a class="nav-link" href="/login/">Login</a>
</li>
{% endif %}
</ul>
</div>
</div>
Expand Down
15 changes: 11 additions & 4 deletions frontend/components/Loading.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import React from "react";
import Logo_Gif from "../images/gif/rec-animation-english---Copy.gif";

export class Loading extends React.Component {
render() {
const { fancy } = this.props;

return (
<div className="text-center">
<div className="spinner-border" role="status">
<span className="visually-hidden-focusable">Loading...</span>
</div>
<div className="d-flex justify-content-center align-items-center" style={{ height: '100%' }}>
{fancy ? (
<img src={Logo_Gif} alt="Loading..." style={{maxWidth: '100%', maxHeight: '100%', objectFit: 'contain'}} />
) : (
<div className="spinner-border" role="status">
<span className="visually-hidden-focusable">Loading...</span>
</div>
)}
</div>
);
}
Expand Down
116 changes: 99 additions & 17 deletions frontend/pages/Explore.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,39 @@ export class Explore extends React.Component {
selectedTag: "All",
selectedMapSquare: "All",
photos: [],
isLoading: false
isLoading: false,
currentPage: 1,
desiredPage: 1,
totalPages: -1,
totalCount: -1,
pageSize: 10,
};
}

handleChangeTags = (event) => {
this.setState({ selectedTag: event.target.value }, this.fetchData);
};

handleChangeMapSquares = (event) => {
this.setState({ selectedMapSquare: event.target.value }, this.fetchData);
handlePageSizeChange = (size) => {
this.setState({ pageSize: size }, this.fetchData);
};

handleDesiredPageChange = (event) => {
this.setState({ desiredPage: event.target.value });
};

handleGoToPage = () => {
const { desiredPage, totalPages } = this.state;
if (desiredPage >= 1 && desiredPage <= totalPages) {
this.setState({ currentPage: desiredPage }, this.fetchData);
} else {
console.warn('Desired page out of range');
}
};

fetchData = async () => {
this.setState({ isLoading: true });
console.log('fetching page', this.state.currentPage);

try {
const response = await fetch('/api/explore/', {
Expand All @@ -34,12 +53,20 @@ export class Explore extends React.Component {
},
body: JSON.stringify({
selectedTag: this.state.selectedTag,
selectedMapSquare: this.state.selectedMapSquare
page: this.state.currentPage,
pageSize: this.state.pageSize,
})
});

const data = await response.json();
this.setState({ photos: data.photos, isLoading: false });
console.log(data);
this.setState({
photos: data.photos,
totalCount: data.totalCount,
totalPages: data.totalPages,
isLoading: false
});

} catch (e) {
console.log(e);
this.setState({ isLoading: false });
Expand All @@ -50,13 +77,59 @@ export class Explore extends React.Component {
this.fetchData();
};

handlePageChange(direction) {
if (direction === 'next') {
this.setState(prevState => ({ currentPage: prevState.currentPage + 1 }), this.fetchData);
} else if (direction === 'previous') {
this.setState(prevState => ({ currentPage: prevState.currentPage - 1 }), this.fetchData);
}
}

hasPreviousPage() { return this.state.currentPage > 1; }

hasNextPage() { return this.state.currentPage < this.state.totalPages; }

render() {
const { objects, arrondissements } = this.props;
const { selectedTag, selectedMapSquare, photos, isLoading } = this.state;
const { objects } = this.props;
const { selectedTag, photos, isLoading, currentPage, totalCount, pageSize } = this.state;
const totalPages = Math.ceil(totalCount / pageSize);

const startImage = (currentPage - 1) * pageSize + 1;
const endImage = Math.min(currentPage * pageSize, totalCount); // Using min to ensure we don't exceed totalCount

const paginationControls = (
<div className="pagination-controls d-flex justify-content-between align-items-center">
{this.hasPreviousPage()
? <button className="btn btn-primary" onClick={() => this.handlePageChange('previous')}>Previous Page</button>
: <div></div>
}
<div className="text-center">
<p>
Page {currentPage} of {totalPages}<br/>
Showing Photos {startImage} - {endImage} out of {totalCount}
</p>
<div className="d-flex align-items-center justify-content-center">
<label>Go to Page: </label>
<input
type="number"
className="form-control mx-4"
value={this.state.desiredPage}
onChange={this.handleDesiredPageChange}
style={{ width: '60px' }}
/>
<button className="btn btn-secondary ml-2" onClick={this.handleGoToPage}>Go</button>
</div>
</div>
{this.hasNextPage() &&
<button className="btn btn-primary" onClick={() => this.handlePageChange('next')}>Next Page</button>
}
</div>
);


return (
<section className="explore">
<div className="row">
<div className="row gx-0">
<div className="sidebar-container col-4">
<div className="sidebar" style={{backgroundImage: `url(${SearchBgTopLeft})`, backgroundPosition: 'top left', backgroundRepeat: 'no-repeat'}}>
<div className="form-group mb-2">
Expand All @@ -67,19 +140,27 @@ export class Explore extends React.Component {
</select>
</div>

<div className="form-group">
<label htmlFor="formMapSquares">Arrondissement</label>
<select className="form-control" value={selectedMapSquare} onChange={this.handleChangeMapSquares}>
<option key="All" value="All">All</option>
{arrondissements.map(arr => <option key={arr} value={arr}>{arr}</option>)}
</select>
<div className="form-group mb-2">
<label>Images per Page</label>
<div>
<button className={`btn ${this.state.pageSize === 10 ? 'btn-primary' : 'btn-light'}`} onClick={() => this.handlePageSizeChange(10)}>10</button>
<button className={`btn ml-2 ${this.state.pageSize === 25 ? 'btn-primary' : 'btn-light'}`} onClick={() => this.handlePageSizeChange(25)}>25</button>
<button className={`btn ml-2 ${this.state.pageSize === 50 ? 'btn-primary' : 'btn-light'}`} onClick={() => this.handlePageSizeChange(50)}>50</button>
</div>
</div>

<p className="explainer">
Use these tools to sort photos according to subjects and location. The object detection tool uses
the YOLO (You Only Look Once) algorithm. Click on any photo to view it and its metadata,
where you will also find a gateway to the similarity algorithm.
</p>
</div>
</div>
<div className="photos col-8 p-5" style={{backgroundImage: `url(${SearchBgTopRight})`, backgroundPosition: 'top right', backgroundRepeat: 'no-repeat'}}>
<div className="photos col-8">
{isLoading ? (
<Loading />
) : (
) : (<>
{paginationControls}
<div className="row">
{photos.length > 0 && photos.map(photo => (
<div className="col-4" key={photo.id}>
Expand All @@ -93,7 +174,8 @@ export class Explore extends React.Component {
</div>
))}
</div>
)}
{paginationControls}
</>)}
</div>
</div>
</section>
Expand Down
Loading

0 comments on commit 208ecaf

Please sign in to comment.