CCockrum commited on
Commit
e294146
·
verified ·
1 Parent(s): a85b55a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -722
app.py CHANGED
@@ -603,725 +603,3 @@ def main():
603
 
604
  if __name__ == "__main__":
605
  main()
606
- import os
607
- import functools
608
-
609
- # Set page configuration
610
- st.set_page_config(
611
- page_title="PetMatch - Find Your Perfect Pet",
612
- page_icon="🐾",
613
- layout="centered"
614
- )
615
-
616
- # Custom CSS
617
- st.markdown("""
618
- <style>
619
- .main-header {
620
- font-size: 2.5rem;
621
- color: #ff6b6c;
622
- text-align: center;
623
- margin-bottom: 1rem;
624
- }
625
- .sub-header {
626
- font-size: 1.5rem;
627
- color: #4a4a4a;
628
- text-align: center;
629
- margin-bottom: 2rem;
630
- }
631
- .pet-card {
632
- border-radius: 10px;
633
- border: 1px solid #e0e0e0;
634
- padding: 1rem;
635
- margin-bottom: 1rem;
636
- }
637
- .pet-name {
638
- font-size: 1.3rem;
639
- font-weight: bold;
640
- color: #ff6b6b;
641
- }
642
- .pet-details {
643
- margin-top: 0.5rem;
644
- }
645
- .pet-description {
646
- margin-top: 1rem;
647
- font-style: italic;
648
- }
649
- .tag {
650
- background-color: #808080;
651
- border-radius: 20px;
652
- padding: 0.2rem 0.6rem;
653
- margin-right: 0.3rem;
654
- font-size: 0.8rem;
655
- }
656
- </style>
657
- """, unsafe_allow_html=True)
658
-
659
- # Initialize session state variables
660
- if 'access_token' not in st.session_state:
661
- st.session_state.access_token = None
662
- if 'token_expires' not in st.session_state:
663
- st.session_state.token_expires = 0
664
- if 'search_results' not in st.session_state:
665
- st.session_state.search_results = None
666
- if 'selected_pet' not in st.session_state:
667
- st.session_state.selected_pet = None
668
- if 'page' not in st.session_state:
669
- st.session_state.page = 1
670
- if 'favorites' not in st.session_state:
671
- st.session_state.favorites = []
672
-
673
- # Function to get access token
674
- def get_access_token():
675
- # Check if token is still valid
676
- if st.session_state.access_token and time.time() < st.session_state.token_expires:
677
- return st.session_state.access_token
678
-
679
- # Get API credentials from environment variables or secrets
680
- api_key = os.environ.get('PETFINDER_API_KEY') or st.secrets.get('PETFINDER_API_KEY')
681
- api_secret = os.environ.get('PETFINDER_API_SECRET') or st.secrets.get('PETFINDER_API_SECRET')
682
-
683
- if not api_key or not api_secret:
684
- st.error("⚠️ Petfinder API credentials are missing. Please set them in your environment variables or Streamlit secrets.")
685
- return None
686
-
687
- # Get new token
688
- url = "https://api.petfinder.com/v2/oauth2/token"
689
- data = {
690
- "grant_type": "client_credentials",
691
- "client_id": api_key,
692
- "client_secret": api_secret
693
- }
694
-
695
- try:
696
- response = requests.post(url, data=data)
697
- response.raise_for_status()
698
- token_data = response.json()
699
- st.session_state.access_token = token_data['access_token']
700
- st.session_state.token_expires = time.time() + token_data['expires_in'] - 60 # Buffer of 60 seconds
701
- return st.session_state.access_token
702
- except requests.exceptions.RequestException as e:
703
- st.error(f"⚠️ Error getting access token: {str(e)}")
704
- return None
705
-
706
- # Function to get RescueGroups API key
707
- def get_rescuegroups_api_key():
708
- api_key = os.environ.get('RESCUEGROUPS_API_KEY') or st.secrets.get('RESCUEGROUPS_API_KEY')
709
- if not api_key:
710
- st.error("⚠️ RescueGroups API key is missing. Please set it in environment variables or Streamlit secrets.")
711
- return None
712
- return api_key
713
-
714
- # Function to search pets from RescueGroups.org
715
- @functools.lru_cache(maxsize=32)
716
- def search_rescuegroups_pets_cached(type_param, location, distance):
717
- api_key = get_rescuegroups_api_key()
718
- if not api_key:
719
- return None
720
-
721
- url = "https://api.rescuegroups.org/v5/public/animals/search"
722
-
723
- headers = {
724
- "Authorization": api_key,
725
- "Content-Type": "application/vnd.api+json"
726
- }
727
-
728
- body = {
729
- "data": {
730
- "type": "animals",
731
- "attributes": {
732
- "species": type_param,
733
- "locationPostalcode": location,
734
- "distance": distance,
735
- "status": "Available",
736
- "sort": "distance",
737
- "limit": 100
738
- }
739
- }
740
- }
741
-
742
- try:
743
- response = requests.post(url, headers=headers, json=body)
744
- response.raise_for_status()
745
- return response.json()
746
- except requests.exceptions.RequestException as e:
747
- st.error(f"⚠️ Error searching pets from RescueGroups: {str(e)}")
748
- return None
749
-
750
- def search_rescuegroups_pets(params):
751
- return search_rescuegroups_pets_cached(params.get("type", "Dog"), params.get("location", ""), params.get("distance", 50))
752
-
753
- # Function to normalize RescueGroups results
754
- def normalize_rescuegroups_data(rg_data):
755
- pets = []
756
- if not rg_data or 'data' not in rg_data:
757
- return pets
758
-
759
- for item in rg_data['data']:
760
- pet = {
761
- 'id': f"RG-{item['id']}",
762
- 'name': item['attributes'].get('name', 'Unknown'),
763
- 'status': 'adoptable',
764
- 'age': item['attributes'].get('ageGroup', ''),
765
- 'gender': item['attributes'].get('sex', ''),
766
- 'size': item['attributes'].get('sizeGroup', ''),
767
- 'breeds': {
768
- 'primary': item['attributes'].get('breedPrimary', ''),
769
- 'secondary': item['attributes'].get('breedSecondary', ''),
770
- 'mixed': False
771
- },
772
- 'colors': {
773
- 'primary': item['attributes'].get('colorPrimary', ''),
774
- 'secondary': item['attributes'].get('colorSecondary', ''),
775
- 'tertiary': ''
776
- },
777
- 'photos': [],
778
- 'description': item['attributes'].get('description', ''),
779
- 'contact': {
780
- 'email': '',
781
- 'phone': '',
782
- 'address': {
783
- 'city': '',
784
- 'state': '',
785
- 'postcode': ''
786
- }
787
- },
788
- 'source': 'RescueGroups'
789
- }
790
- if 'photos' in item['attributes'] and item['attributes']['photos']:
791
- pet['photos'] = [{'medium': item['attributes']['photos'][0]}]
792
- pets.append(pet)
793
- return pets
794
-
795
- # Example modification in your search form to call BOTH APIs
796
- with st.form("pet_search_form"):
797
- # ... your existing form fields ...
798
- submitted = st.form_submit_button("Search")
799
-
800
- if submitted:
801
- params = {
802
- "type": animal_type.split(" ")[0],
803
- "location": location,
804
- "distance": distance
805
- }
806
-
807
- petfinder_results = search_pets(params)
808
- rescuegroups_results = search_rescuegroups_pets(params)
809
-
810
- combined_pets = []
811
-
812
- if petfinder_results and 'animals' in petfinder_results:
813
- for pet in petfinder_results['animals']:
814
- pet['source'] = 'Petfinder'
815
- combined_pets.append(pet)
816
- if rescuegroups_results:
817
- normalized = normalize_rescuegroups_data(rescuegroups_results)
818
- combined_pets.extend(normalized)
819
-
820
- if combined_pets:
821
- st.session_state.search_results = {'animals': combined_pets}
822
- st.session_state.page = 1
823
- st.success(f"Found {len(combined_pets)} pets from both sources!")
824
- else:
825
- st.error("No pets found. Try adjusting your filters.")
826
-
827
-
828
-
829
- # Function to search pets
830
- def search_pets(params):
831
- token = get_access_token()
832
- if not token:
833
- return None
834
-
835
- url = "https://api.petfinder.com/v2/animals"
836
- headers = {"Authorization": f"Bearer {token}"}
837
-
838
- try:
839
- response = requests.get(url, headers=headers, params=params)
840
- response.raise_for_status()
841
- return response.json()
842
- except requests.exceptions.RequestException as e:
843
- st.error(f"⚠️ Error searching pets: {str(e)}")
844
- return None
845
-
846
- # Function to get breeds
847
- def get_breeds(animal_type):
848
- token = get_access_token()
849
- if not token:
850
- return []
851
-
852
- url = f"https://api.petfinder.com/v2/types/{animal_type}/breeds"
853
- headers = {"Authorization": f"Bearer {token}"}
854
-
855
- try:
856
- response = requests.get(url, headers=headers)
857
- response.raise_for_status()
858
- return [breed['name'] for breed in response.json()['breeds']]
859
- except requests.exceptions.RequestException as e:
860
- st.error(f"⚠️ Error getting breeds: {str(e)}")
861
- return []
862
-
863
- # Function to get organizations
864
- def get_organizations(location):
865
- token = get_access_token()
866
- if not token:
867
- return []
868
-
869
- url = "https://api.petfinder.com/v2/organizations"
870
- headers = {"Authorization": f"Bearer {token}"}
871
- params = {"location": location, "distance": 100, "limit": 100}
872
-
873
- try:
874
- response = requests.get(url, headers=headers, params=params)
875
- response.raise_for_status()
876
- return [(org['id'], org['name']) for org in response.json()['organizations']]
877
- except requests.exceptions.RequestException as e:
878
- st.error(f"⚠️ Error getting organizations: {str(e)}")
879
- return []
880
-
881
- # Function to get pet details
882
- def get_pet_details(pet_id):
883
- token = get_access_token()
884
- if not token:
885
- return None
886
-
887
- url = f"https://api.petfinder.com/v2/animals/{pet_id}"
888
- headers = {"Authorization": f"Bearer {token}"}
889
-
890
- try:
891
- response = requests.get(url, headers=headers)
892
- response.raise_for_status()
893
- return response.json()['animal']
894
- except requests.exceptions.RequestException as e:
895
- st.error(f"⚠️ Error getting pet details: {str(e)}")
896
- return None
897
-
898
- # Function to format pet card
899
- def display_pet_card(pet, is_favorite=False, context="search"):
900
- col1, col2 = st.columns([1, 2])
901
-
902
- with col1:
903
- if pet['photos'] and len(pet['photos']) > 0:
904
- st.image(pet['photos'][0]['medium'], use_container_width=True)
905
- else:
906
- st.image("https://via.placeholder.com/300x300?text=No+Image", use_container_width=True)
907
-
908
- with col2:
909
- st.markdown(f"<div class='pet-name'>{pet['name']}</div>", unsafe_allow_html=True)
910
-
911
- # Tags
912
- tags_html = ""
913
- if pet['status'] == 'adoptable':
914
- tags_html += "<span class='tag' style='background-color: #808080;'>Adoptable</span> "
915
- else:
916
- tags_html += f"<span class='tag' style='background-color: #808080;'>{pet['status'].title()}</span> "
917
-
918
- if pet['age']:
919
- tags_html += f"<span class='tag'>{pet['age']}</span> "
920
- if pet['gender']:
921
- tags_html += f"<span class='tag'>{pet['gender']}</span> "
922
- if pet['size']:
923
- tags_html += f"<span class='tag'>{pet['size']}</span> "
924
-
925
- st.markdown(f"<div>{tags_html}</div>", unsafe_allow_html=True)
926
-
927
- st.markdown("<div class='pet-details'>", unsafe_allow_html=True)
928
- if pet['breeds']['primary']:
929
- breed_text = pet['breeds']['primary']
930
- if pet['breeds']['secondary']:
931
- breed_text += f" & {pet['breeds']['secondary']}"
932
- if pet['breeds']['mixed']:
933
- breed_text += " (Mixed)"
934
- st.markdown(f"<strong>Breed:</strong> {breed_text}", unsafe_allow_html=True)
935
-
936
- if pet['colors']['primary'] or pet['colors']['secondary'] or pet['colors']['tertiary']:
937
- colors = [c for c in [pet['colors']['primary'], pet['colors']['secondary'], pet['colors']['tertiary']] if c]
938
- st.markdown(f"<strong>Colors:</strong> {', '.join(colors)}", unsafe_allow_html=True)
939
-
940
- if 'location' in pet and pet['contact']['address']['city'] and pet['contact']['address']['state']:
941
- st.markdown(f"<strong>Location:</strong> {pet['contact']['address']['city']}, {pet['contact']['address']['state']}", unsafe_allow_html=True)
942
-
943
- st.markdown("</div>", unsafe_allow_html=True)
944
-
945
- if pet['description']:
946
- st.markdown(f"<div class='pet-description'>{pet['description'][:500]}{'...' if len(pet['description']) > 500 else ''}</div>", unsafe_allow_html=True)
947
-
948
- col1, col2 = st.columns(2)
949
- with col1:
950
- if st.button("View Details", key=f"details_{context}_{pet['id']}"):
951
- st.session_state.selected_pet = pet['id']
952
- st.rerun()
953
- with col2:
954
- if not is_favorite:
955
- if st.button("Add to Favorites", key=f"fav_{context}_{pet['id']}"):
956
- if pet['id'] not in [p['id'] for p in st.session_state.favorites]:
957
- st.session_state.favorites.append(pet)
958
- st.success(f"Added {pet['name']} to favorites!")
959
- st.rerun()
960
- else:
961
- if st.button("Remove from Favorites", key=f"unfav_{context}_{pet['id']}"):
962
- st.session_state.favorites = [p for p in st.session_state.favorites if p['id'] != pet['id']]
963
- st.success(f"Removed {pet['name']} from favorites!")
964
- st.rerun()
965
-
966
- # Function to generate pet compatibility message
967
- def get_compatibility_message(pet):
968
- messages = []
969
-
970
- # Check for kids
971
- if 'children' in pet['environment'] and pet['environment']['children'] is not None:
972
- if pet['environment']['children']:
973
- messages.append("✅ Good with children")
974
- else:
975
- messages.append("❌ Not recommended for homes with children")
976
-
977
- # Check for dogs
978
- if 'dogs' in pet['environment'] and pet['environment']['dogs'] is not None:
979
- if pet['environment']['dogs']:
980
- messages.append("✅ Good with dogs")
981
- else:
982
- messages.append("❌ Not recommended for homes with dogs")
983
-
984
- # Check for cats
985
- if 'cats' in pet['environment'] and pet['environment']['cats'] is not None:
986
- if pet['environment']['cats']:
987
- messages.append("✅ Good with cats")
988
- else:
989
- messages.append("❌ Not recommended for homes with cats")
990
-
991
- # Handling care needs
992
- if pet['attributes']:
993
- if 'special_needs' in pet['attributes'] and pet['attributes']['special_needs']:
994
- messages.append("⚠️ Has special needs")
995
-
996
- if 'house_trained' in pet['attributes'] and pet['attributes']['house_trained']:
997
- messages.append("✅ House-trained")
998
- elif 'house_trained' in pet['attributes']:
999
- messages.append("❌ Not house-trained")
1000
-
1001
- if 'shots_current' in pet['attributes'] and pet['attributes']['shots_current']:
1002
- messages.append("✅ Vaccinations up to date")
1003
-
1004
- if 'spayed_neutered' in pet['attributes'] and pet['attributes']['spayed_neutered']:
1005
- messages.append("✅ Spayed/neutered")
1006
-
1007
- return messages
1008
-
1009
- # Function to display pet details page
1010
- # Changes to make keys unique across different tabs
1011
-
1012
- # Function to display pet details page with unique tab identifier
1013
- def display_pet_details(pet_id, context="search", tab_id="tab1"):
1014
- pet = get_pet_details(pet_id)
1015
- if not pet:
1016
- st.error("Unable to retrieve pet details. Please try again.")
1017
- return
1018
-
1019
- # Back button with unique key that includes tab identifier
1020
- if st.button("← Back to Search Results", key=f"back_{tab_id}_{context}_{pet_id}"):
1021
- st.session_state.selected_pet = None
1022
- st.rerun() # Force immediate rerun
1023
-
1024
- # Pet name and status
1025
- st.markdown(f"<h1 class='main-header'>{pet['name']}</h1>", unsafe_allow_html=True)
1026
-
1027
- status_color = "#c8e6c9" if pet['status'] == 'adoptable' else "#ffcdd2"
1028
- st.markdown(f"<div style='text-align: center;'><span class='tag' style='background-color: {status_color}; font-size: 1rem;'>{pet['status'].title()}</span></div>", unsafe_allow_html=True)
1029
-
1030
- # Pet photos
1031
- if pet['photos'] and len(pet['photos']) > 0:
1032
- photo_cols = st.columns(min(3, len(pet['photos'])))
1033
- for i, col in enumerate(photo_cols):
1034
- if i < len(pet['photos']):
1035
- col.image(pet['photos'][i]['large'], use_container_width=True)
1036
- else:
1037
- st.image("https://via.placeholder.com/500x300?text=No+Image", use_container_width=True)
1038
-
1039
- # Pet details
1040
- col1, col2 = st.columns(2)
1041
-
1042
- with col1:
1043
- st.markdown("### Details")
1044
- # Fix the breed line
1045
- breed_text = pet['breeds']['primary']
1046
- if pet['breeds']['secondary']:
1047
- breed_text += f" & {pet['breeds']['secondary']}"
1048
- if pet['breeds']['mixed']:
1049
- breed_text += " (Mixed)"
1050
-
1051
- details = [
1052
- f"**Type:** {pet['type']}",
1053
- f"**Breed:** {breed_text}",
1054
- f"**Age:** {pet['age']}",
1055
- f"**Gender:** {pet['gender']}",
1056
- f"**Size:** {pet['size']}"
1057
- ]
1058
-
1059
- # Fix the colors line as well, to be safe
1060
- colors = [c for c in [pet['colors']['primary'], pet['colors']['secondary'], pet['colors']['tertiary']] if c]
1061
- if colors:
1062
- details.append(f"**Colors:** {', '.join(colors)}")
1063
-
1064
- for detail in details:
1065
- st.markdown(detail)
1066
-
1067
- with col2:
1068
- st.markdown("### Compatibility")
1069
- compatibility = get_compatibility_message(pet)
1070
- for msg in compatibility:
1071
- st.markdown(msg)
1072
-
1073
- # Description
1074
- if pet['description']:
1075
- if pet['description']:
1076
- st.markdown("### About")
1077
- #st.markdown(pet['description'])
1078
- st.markdown(f"<div class='pet-description'>{pet['description'][:500]}{'...' if len(pet['description']) > 500 else ''}</div>", unsafe_allow_html=True)
1079
-
1080
- # Contact information
1081
- st.markdown("### Adoption Information")
1082
-
1083
- # Organization info
1084
- if pet['organization_id']:
1085
- st.markdown(f"**Organization:** {pet['organization_id']}")
1086
-
1087
- # Contact details
1088
- contact_info = []
1089
- if pet['contact']['email']:
1090
- contact_info.append(f"**Email:** {pet['contact']['email']}")
1091
- if pet['contact']['phone']:
1092
- contact_info.append(f"**Phone:** {pet['contact']['phone']}")
1093
- if pet['contact']['address']['city'] and pet['contact']['address']['state']:
1094
- contact_info.append(f"**Location:** {pet['contact']['address']['city']}, {pet['contact']['address']['state']} {pet['contact']['address']['postcode'] or ''}")
1095
-
1096
- for info in contact_info:
1097
- st.markdown(info)
1098
-
1099
- # URL to pet on Petfinder
1100
- if pet['url']:
1101
- st.markdown(f"[View on Petfinder]({pet['url']})")
1102
-
1103
- # Add to favorites with unique key
1104
- is_favorite = pet['id'] in [p['id'] for p in st.session_state.favorites]
1105
- if not is_favorite:
1106
- if st.button("Add to Favorites", key=f"add_fav_{tab_id}_{context}_{pet_id}"):
1107
- st.session_state.favorites.append(pet)
1108
- st.success(f"Added {pet['name']} to favorites!")
1109
- st.rerun()
1110
- else:
1111
- if st.button("Remove from Favorites", key=f"rem_fav_{tab_id}_{context}_{pet_id}"):
1112
- st.session_state.favorites = [p for p in st.session_state.favorites if p['id'] != pet['id']]
1113
- st.success(f"Removed {pet['name']} from favorites!")
1114
- st.rerun()
1115
-
1116
- # Function to format pet card with unique tab identifier
1117
- def display_pet_card(pet, is_favorite=False, context="search", tab_id="tab1"):
1118
- col1, col2 = st.columns([1, 2])
1119
-
1120
- with col1:
1121
- if pet['photos'] and len(pet['photos']) > 0:
1122
- st.image(pet['photos'][0]['medium'], use_container_width=True)
1123
- else:
1124
- st.image("https://via.placeholder.com/300x300?text=No+Image", use_container_width=True)
1125
-
1126
- with col2:
1127
- st.markdown(f"<div class='pet-name'>{pet['name']}</div>", unsafe_allow_html=True)
1128
-
1129
- # Tags
1130
- tags_html = ""
1131
- if pet['status'] == 'adoptable':
1132
- tags_html += "<span class='tag' style='background-color: #808080;'>Adoptable</span> "
1133
- else:
1134
- tags_html += f"<span class='tag' style='background-color: #808080;'>{pet['status'].title()}</span> "
1135
-
1136
- if pet['age']:
1137
- tags_html += f"<span class='tag'>{pet['age']}</span> "
1138
- if pet['gender']:
1139
- tags_html += f"<span class='tag'>{pet['gender']}</span> "
1140
- if pet['size']:
1141
- tags_html += f"<span class='tag'>{pet['size']}</span> "
1142
-
1143
- st.markdown(f"<div>{tags_html}</div>", unsafe_allow_html=True)
1144
-
1145
- st.markdown("<div class='pet-details'>", unsafe_allow_html=True)
1146
- if pet['breeds']['primary']:
1147
- breed_text = pet['breeds']['primary']
1148
- if pet['breeds']['secondary']:
1149
- breed_text += f" & {pet['breeds']['secondary']}"
1150
- if pet['breeds']['mixed']:
1151
- breed_text += " (Mixed)"
1152
- st.markdown(f"<strong>Breed:</strong> {breed_text}", unsafe_allow_html=True)
1153
-
1154
- if pet['colors']['primary'] or pet['colors']['secondary'] or pet['colors']['tertiary']:
1155
- colors = [c for c in [pet['colors']['primary'], pet['colors']['secondary'], pet['colors']['tertiary']] if c]
1156
- st.markdown(f"<strong>Colors:</strong> {', '.join(colors)}", unsafe_allow_html=True)
1157
-
1158
- if 'location' in pet and pet['contact']['address']['city'] and pet['contact']['address']['state']:
1159
- st.markdown(f"<strong>Location:</strong> {pet['contact']['address']['city']}, {pet['contact']['address']['state']}", unsafe_allow_html=True)
1160
-
1161
- st.markdown("</div>", unsafe_allow_html=True)
1162
-
1163
- if pet['description']:
1164
- st.markdown(f"<div class='pet-description'>{pet['description'][:300]}{'...' if len(pet['description']) > 300 else ''}</div>", unsafe_allow_html=True)
1165
-
1166
- col1, col2 = st.columns(2)
1167
- with col1:
1168
- if st.button("View Details", key=f"details_{tab_id}_{context}_{pet['id']}"):
1169
- st.session_state.selected_pet = pet['id']
1170
- st.rerun()
1171
- with col2:
1172
- if not is_favorite:
1173
- if st.button("Add to Favorites", key=f"fav_{tab_id}_{context}_{pet['id']}"):
1174
- if pet['id'] not in [p['id'] for p in st.session_state.favorites]:
1175
- st.session_state.favorites.append(pet)
1176
- st.success(f"Added {pet['name']} to favorites!")
1177
- st.rerun()
1178
- else:
1179
- if st.button("Remove from Favorites", key=f"unfav_{tab_id}_{context}_{pet['id']}"):
1180
- st.session_state.favorites = [p for p in st.session_state.favorites if p['id'] != pet['id']]
1181
- st.success(f"Removed {pet['name']} from favorites!")
1182
- st.rerun()
1183
-
1184
- # Main app with updated function calls
1185
- def main():
1186
- # Title
1187
- st.markdown("<h1 class='main-header'>🐾 PetMatch</h1>", unsafe_allow_html=True)
1188
- st.markdown("<p class='sub-header'>Find your perfect pet companion</p>", unsafe_allow_html=True)
1189
-
1190
- # Create tabs
1191
- tab1, tab2, tab3 = st.tabs(["Search", "Favorites", "About"])
1192
-
1193
- with tab1:
1194
- # If a pet is selected, show details
1195
- if st.session_state.selected_pet:
1196
- display_pet_details(st.session_state.selected_pet, context="search", tab_id="tab1")
1197
- else:
1198
- # Search form
1199
- with st.expander("Search Options", expanded=True):
1200
- with st.form("pet_search_form"):
1201
- col1, col2 = st.columns(2)
1202
-
1203
- with col1:
1204
- animal_type = st.selectbox(
1205
- "Animal Type",
1206
- ["Dog", "Cat", "Rabbit", "Small & Furry", "Horse", "Bird", "Scales, Fins & Other", "Barnyard"]
1207
- )
1208
-
1209
- location = st.text_input("Location (ZIP code or City, State)", "")
1210
-
1211
- distance = st.slider("Distance (miles)", min_value=10, max_value=500, value=50, step=10)
1212
-
1213
- with col2:
1214
- age_options = ["", "Baby", "Young", "Adult", "Senior"]
1215
- age = st.selectbox("Age", age_options)
1216
-
1217
- size_options = ["", "Small", "Medium", "Large", "XLarge"]
1218
- size = st.selectbox("Size", size_options)
1219
-
1220
- gender_options = ["", "Male", "Female"]
1221
- gender = st.selectbox("Gender", gender_options)
1222
- good_with_children = st.checkbox("Good with children")
1223
- good_with_dogs = st.checkbox("Good with dogs")
1224
- good_with_cats = st.checkbox("Good with cats")
1225
- house_trained = st.checkbox("House-trained")
1226
- special_needs = st.checkbox("Special needs")
1227
-
1228
- submitted = st.form_submit_button("Search")
1229
-
1230
- if submitted:
1231
- # Build search parameters
1232
- params = {
1233
- "type": animal_type.split(" ")[0], # Take first word for types like "Small & Furry"
1234
- "location": location,
1235
- "distance": distance,
1236
- "status": "adoptable",
1237
- "sort": "distance",
1238
- "limit": 100
1239
- }
1240
-
1241
- if age and age != "":
1242
- params["age"] = age
1243
- if size and size != "":
1244
- params["size"] = size
1245
- if gender and gender != "":
1246
- params["gender"] = gender
1247
-
1248
- # Add advanced filters
1249
- if good_with_children:
1250
- params["good_with_children"] = 1
1251
- if good_with_dogs:
1252
- params["good_with_dogs"] = 1
1253
- if good_with_cats:
1254
- params["good_with_cats"] = 1
1255
- if house_trained:
1256
- params["house_trained"] = 1
1257
- if special_needs:
1258
- params["special_needs"] = 1
1259
-
1260
- # Perform search
1261
- results = search_pets(params)
1262
- if results and 'animals' in results:
1263
- st.session_state.search_results = results
1264
- st.session_state.page = 1
1265
- st.success(f"Found {len(results['animals'])} pets!")
1266
- else:
1267
- st.error("No pets found with those criteria. Try expanding your search.")
1268
-
1269
- # Display search results
1270
- if st.session_state.search_results and 'animals' in st.session_state.search_results:
1271
- st.markdown("### Search Results")
1272
-
1273
- # Pagination
1274
- results = st.session_state.search_results['animals']
1275
- total_pages = (len(results) + 9) // 10 # 10 items per page
1276
-
1277
- # Display page selector
1278
- if total_pages > 1:
1279
- col1, col2, col3 = st.columns([1, 3, 1])
1280
- with col2:
1281
- page = st.slider("Page", 1, total_pages, st.session_state.page)
1282
- if page != st.session_state.page:
1283
- st.session_state.page = page
1284
-
1285
- # Display pets for current page
1286
- start_idx = (st.session_state.page - 1) * 10
1287
- end_idx = min(start_idx + 10, len(results))
1288
-
1289
- for pet in results[start_idx:end_idx]:
1290
- st.markdown("---")
1291
- display_pet_card(pet, tab_id="tab1")
1292
-
1293
- with tab2:
1294
- st.markdown("### Your Favorite Pets")
1295
-
1296
- if not st.session_state.favorites:
1297
- st.info("You haven't added any pets to your favorites yet. Start searching to find your perfect match!")
1298
- else:
1299
- # Check if a pet is selected from favorites
1300
- if st.session_state.selected_pet:
1301
- display_pet_details(st.session_state.selected_pet, context="favorites", tab_id="tab2")
1302
- else:
1303
- for pet in st.session_state.favorites:
1304
- st.markdown("---")
1305
- display_pet_card(pet, is_favorite=True, context="favorites", tab_id="tab2")
1306
-
1307
- with tab3:
1308
- st.markdown("### About PetMatch")
1309
- st.markdown("""
1310
- PetMatch helps you find your perfect pet companion from thousands of adoptable animals across the country.
1311
-
1312
- **How to use PetMatch:**
1313
- 1. Search for pets based on your preferences and location
1314
- 2. Browse through the results and click "View Details" to learn more about each pet
1315
- 3. Add pets to your favorites to keep track of the ones you're interested in
1316
- 4. Contact the shelter or rescue organization directly using the provided information
1317
-
1318
- **Data Source:**
1319
- PetMatch uses the Petfinder API to provide up-to-date information on adoptable pets. Petfinder is North America's largest adoption website with hundreds of thousands of adoptable pets listed by more than 11,500 animal shelters and rescue organizations.
1320
-
1321
- **Privacy:**
1322
- PetMatch does not store any personal information or search history. Your favorites are stored locally in your browser and are not shared with any third parties.
1323
- """)
1324
-
1325
-
1326
- if __name__ == "__main__":
1327
- main()
 
603
 
604
  if __name__ == "__main__":
605
  main()